1- # Utilities for the pywin32 tests
1+ """Utilities for the pywin32 tests"""
2+
3+ from __future__ import annotations
4+
25import gc
36import os
47import site
58import sys
69import unittest
10+ from itertools import groupby
11+ from typing import TYPE_CHECKING
712
813import winerror
914
15+ if TYPE_CHECKING :
16+ from _typeshed import OptExcInfo
17+
1018##
1119## unittest related stuff
1220##
@@ -145,10 +153,10 @@ def loadTestsFromName(self, name, module=None):
145153
146154# win32 error codes that probably mean we need to be elevated (ie, if we
147155# aren't elevated, we treat these error codes as 'skipped')
148- non_admin_error_codes = [
156+ non_admin_error_codes = {
149157 winerror .ERROR_ACCESS_DENIED ,
150158 winerror .ERROR_PRIVILEGE_NOT_HELD ,
151- ]
159+ }
152160
153161_is_admin = None
154162
@@ -197,28 +205,19 @@ def find_test_fixture(basename, extra_dir="."):
197205 d = os .path .normcase (d )
198206 if os .path .commonprefix ([this_file , d ]) == d :
199207 # looks like we are in an installed Python, so skip the text.
200- raise TestSkipped (f"Can't find test fixture '{ fname } '" )
208+ raise unittest . SkipTest (f"Can't find test fixture '{ fname } '" )
201209 # Looks like we are running from source, so this is fatal.
202210 raise RuntimeError (f"Can't find test fixture '{ fname } '" )
203211
204212
205- # If this exception is raised by a test, the test is reported as a 'skip'
206- class TestSkipped (Exception ):
207- pass
208-
209-
210213# The 'TestResult' subclass that records the failures and has the special
211- # handling for the TestSkipped exception.
214+ # handling for the unittest.SkipTest exception.
212215class TestResult (unittest .TextTestResult ):
213- def __init__ (self , * args , ** kw ):
214- super ().__init__ (* args , ** kw )
215- self .skips = {} # count of skips for each reason.
216+ def addError (self , test : unittest .TestCase , err : OptExcInfo ) -> None :
217+ """Called when an error has occurred.
216218
217- def addError (self , test , err ):
218- """Called when an error has occurred. 'err' is a tuple of values as
219- returned by sys.exc_info().
219+ Translate a couple of 'well-known' exceptions into 'skipped'
220220 """
221- # translate a couple of 'well-known' exceptions into 'skipped'
222221 import pywintypes
223222
224223 exc_val = err [1 ]
@@ -229,42 +228,28 @@ def addError(self, test, err):
229228 and exc_val .winerror in non_admin_error_codes
230229 and not check_is_admin ()
231230 ):
232- exc_val = TestSkipped (exc_val )
231+ return self . addSkip ( test , str (exc_val ) )
233232 # and COM errors due to objects not being registered (the com test
234233 # suite will attempt to catch this and handle it itself if the user
235234 # is admin)
236- elif isinstance (exc_val , pywintypes .com_error ) and exc_val .hresult in [
235+ elif isinstance (exc_val , pywintypes .com_error ) and exc_val .hresult in {
237236 winerror .CO_E_CLASSSTRING ,
238237 winerror .REGDB_E_CLASSNOTREG ,
239238 winerror .TYPE_E_LIBNOTREGISTERED ,
240- ]:
241- exc_val = TestSkipped (exc_val )
242- # NotImplemented generally means the platform doesn't support the
243- # functionality.
239+ }:
240+ return self .addSkip (test , str (exc_val ))
241+ # NotImplemented generally means the platform doesn't support the functionality.
244242 elif isinstance (exc_val , NotImplementedError ):
245- exc_val = TestSkipped (NotImplementedError )
246-
247- if isinstance (exc_val , TestSkipped ):
248- reason = exc_val .args [0 ]
249- # if the reason itself is another exception, get its args.
250- try :
251- reason = tuple (reason .args )
252- except (AttributeError , TypeError ):
253- pass
254- self .skips .setdefault (reason , 0 )
255- self .skips [reason ] += 1
256- if self .showAll :
257- self .stream .writeln (f"SKIP ({ reason } )" )
258- elif self .dots :
259- self .stream .write ("S" )
260- self .stream .flush ()
261- return
243+ return self .addSkip (test , str (exc_val ))
244+
262245 super ().addError (test , err )
263246
264- def printErrors (self ):
247+ def printErrors (self ) -> None :
265248 super ().printErrors ()
266- for reason , num_skipped in self .skips .items ():
267- self .stream .writeln ("SKIPPED: %d tests - %s" % (num_skipped , reason ))
249+ reasons = [reason for (test , reason ) in self .skipped ]
250+ reason_counts = [(reason , reasons .count (reason )) for reason in set (reasons )]
251+ for reason , num_skipped in reason_counts :
252+ self .stream .writeln (f"SKIPPED: { num_skipped } tests - { reason } " )
268253
269254
270255# TestRunner subclass necessary just to get our TestResult hooked up.
0 commit comments