1+ from __future__ import annotations
2+
13import getopt
24import os
35import re
6+ import shutil
47import sys
58import traceback
69import 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
1414
1515import win32com
1616
17+ this_folder = os .path .dirname (__file__ )
18+ win32com_src_dir = os .path .abspath (os .path .join (this_folder , ".." ))
1719# We'd prefer the win32com namespace to be the parent of __file__ - ie, our source-tree,
1820# rather than the version installed - otherwise every .py change needs a full install to
1921# test!
2022# 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 :)
2224win32com .__path__ [0 ] = win32com_src_dir
2325
2426import pythoncom
25- import win32com .client
27+ import win32com .client . gencache
2628from pywin32_testutil import TestLoader , TestRunner
2729from win32com .test .util import (
2830 CapturingFunctionTestCase ,
3537verbosity = 1 # default unittest verbosity.
3638
3739
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 :
5341 if os .path .isdir (win32com .__gen_path__ ):
5442 if verbosity > 1 :
55- print ("Deleting files from %s" % ( win32com .__gen_path__ ) )
43+ print (f "Deleting files from { win32com .__gen_path__ } " )
5644 shutil .rmtree (win32com .__gen_path__ )
57- import win32com .client .gencache
5845
5946 win32com .client .gencache .__init__ () # Reset
6047
6148
62- def RemoveRefCountOutput (data ) :
63- while 1 :
49+ def RemoveRefCountOutput (data : str ) -> str :
50+ while True :
6451 last_line_pos = data .rfind ("\n " )
6552 if not re .match (r"\[\d+ refs\]" , data [last_line_pos + 1 :]):
6653 break
@@ -72,47 +59,45 @@ def RemoveRefCountOutput(data):
7259 return data
7360
7461
75- def ExecuteSilentlyIfOK (cmd , testcase ) :
62+ def ExecuteSilentlyIfOK (cmd : str , testcase : TestCase ) -> str :
7663 f = os .popen (cmd )
7764 data = f .read ().strip ()
7865 rc = f .close ()
7966 if rc :
8067 print (data )
81- testcase .fail ("Executing '%s ' failed (%d)" % ( cmd , rc ) )
68+ testcase .fail (f "Executing '{ cmd } ' failed ({ rc } )" )
8269 # for "_d" builds, strip the '[xxx refs]' line
8370 return RemoveRefCountOutput (data )
8471
8572
8673class PyCOMTest (TestCase ):
8774 no_leak_tests = True # done by the test itself
8875
89- def testit (self ):
76+ def testit (self ) -> None :
9077 # Check that the item is registered, so we get the correct
9178 # 'skipped' behaviour (and recorded as such) rather than either
9279 # error or silence due to non-registration.
9380 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" ),
9782 "Python.Test.PyCOMTest" ,
9883 )
9984
10085 # Execute testPyComTest in its own process so it can play
10186 # 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" )
10388 cmd = f'{ sys .executable } "{ fname } " -q 2>&1'
10489 data = ExecuteSilentlyIfOK (cmd , self )
10590
10691
10792class PippoTest (TestCase ):
108- def testit (self ):
93+ def testit (self ) -> None :
10994 # Check we are registered before spawning the process.
11095 from win32com .test import pippo_server
11196
11297 RegisterPythonServer (pippo_server .__file__ , "Python.Test.Pippo" )
11398
11499 python = sys .executable
115- fname = os .path .join (os . path . dirname ( this_file ) , "testPippo.py" )
100+ fname = os .path .join (this_folder , "testPippo.py" )
116101 cmd = f'{ python } "{ fname } " 2>&1'
117102 ExecuteSilentlyIfOK (cmd , self )
118103
@@ -121,35 +106,44 @@ def testit(self):
121106# function in that module if the module isn't unitest based...
122107unittest_modules = [
123108 # 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+ ],
131125 # Level 2 tests - wants our demo COM objects registered.
132126 # (these are strange; on github CI they get further than expected when
133127 # our objects are not installed, so fail to quietly fail with "can't
134128 # 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" ],
138130 # 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+ ],
142138 # Level 4 tests - we try and run `makepy` over every typelib installed!
143- """testmakepy.TestAll
144- """ .split (),
139+ ["testmakepy.TestAll" ],
145140]
146141
147142# A list of other unittest modules we use - these are fully qualified module
148143# names and the module is assumed to be unittest based.
149144unittest_other_modules = [
150145 # Level 1 tests.
151- """win32com.directsound.test.ds_test
152- """ .split (),
146+ ["win32com.directsound.test.ds_test" ],
153147 # Level 2 tests.
154148 [],
155149 # Level 3 tests.
@@ -192,7 +186,9 @@ def testit(self):
192186]
193187
194188
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 ]:
196192 if test_name .find ("." ) > 0 :
197193 mod_name , func_name = test_name .split ("." )
198194 else :
@@ -202,19 +198,21 @@ def get_test_mod_and_func(test_name, import_failures):
202198 try :
203199 __import__ (fq_mod_name )
204200 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 ))
207203 return None , None
208204 func = None if func_name is None else getattr (mod , func_name )
209205 return mod , func
210206
211207
212208# 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 ]]]:
214212 suite = unittest .TestSuite ()
215- import_failures = []
213+ import_failures : list [ tuple [ str , BaseException ]] = []
216214 loader = TestLoader ()
217- for i in range (testLevel ):
215+ for i in range (test_level ):
218216 for mod_name in unittest_modules [i ]:
219217 mod , func = get_test_mod_and_func (mod_name , import_failures )
220218 if mod is None :
@@ -234,12 +232,12 @@ def make_test_suite(test_level=1):
234232 for test_class in custom_test_cases [i ]:
235233 suite .addTest (unittest .defaultTestLoader .loadTestsFromTestCase (test_class ))
236234 # other "normal" unittest modules.
237- for i in range (testLevel ):
235+ for i in range (test_level ):
238236 for mod_name in unittest_other_modules [i ]:
239237 try :
240238 __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 ))
243241 continue
244242
245243 mod = sys .modules [mod_name ]
@@ -253,13 +251,14 @@ def make_test_suite(test_level=1):
253251 return suite , import_failures
254252
255253
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-3. Level 1 tests are quick,
261+ level 2 tests invoke Word, IE etc, level 3 take ages!""" )
263262 sys .exit (1 )
264263
265264
@@ -271,44 +270,47 @@ def usage(why):
271270 for opt , val in opts :
272271 if opt == "-v" :
273272 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 :
273+
274+ if len (args ) > 1 :
275+ usage (
276+ "Only a single test level argument is supported. "
277+ + "Test names are not supported yet"
278+ )
279+ try :
280+ test_level = int (args [0 ])
281+ except IndexError :
282+ test_level = 2 # default to quick test with local objects
283+ except ValueError :
284284 usage ("Test names are not supported yet" )
285+ if test_level < 1 or test_level > 4 :
286+ usage ("Only levels 1-4 are supported" )
285287 CleanGenerated ()
286288
287- suite , import_failures = make_test_suite (testLevel )
289+ suite , import_failures = make_test_suite (test_level )
288290 if verbosity :
289291 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)" )
292+ print (
293+ dedent ("""\
294+ This is a debug build - memory leak tests will also be run.
295+ These tests may take *many* minutes to run - be patient!
296+ (running from python.exe will avoid these leak tests""" )
297+ )
293298 print (
294- "Executing level %d tests - %d test cases will be run "
295- % ( testLevel , suite .countTestCases ())
299+ f "Executing level { test_level } tests "
300+ + f"- { suite .countTestCases ()} test cases will be run"
296301 )
297302 if verbosity == 1 and suite .countTestCases () < 70 :
298303 # A little row of markers so the dots show how close to finished
299304 print ("|" * suite .countTestCases ())
300- testRunner = TestRunner (verbosity = verbosity )
301- testResult = testRunner .run (suite )
305+ testResult = TestRunner (verbosity = verbosity ).run (suite )
302306 if import_failures :
303- testResult .stream .writeln (
307+ testResult .stream .write (
304308 "*** The following test modules could not be imported ***"
305309 )
306- for mod_name , ( exc_type , exc_val ) in import_failures :
307- desc = "\n " .join (traceback .format_exception_only (exc_type , exc_val ))
310+ for mod_name , error in import_failures :
311+ desc = "\n " .join (traceback .format_exception_only (type ( error ), error ))
308312 testResult .stream .write (f"{ mod_name } : { desc } " )
309- testResult .stream .writeln (
310- "*** %d test(s) could not be run ***" % len (import_failures )
311- )
313+ testResult .stream .write (f"*** { import_failures } test(s) could not be run ***" )
312314
313315 # re-print unit-test error here so it is noticed
314316 if not testResult .wasSuccessful ():
0 commit comments