11# Ridiculously simple test of the winsound module for Windows.
22
3- import unittest
4- from test import support
5- support .requires ('audio' )
6- import time
3+ import functools
74import os
85import subprocess
6+ import time
7+ import unittest
8+
9+ from test import support
910
11+ support .requires ('audio' )
1012winsound = support .import_module ('winsound' )
11- ctypes = support .import_module ('ctypes' )
12- import winreg
13-
14- def has_sound (sound ):
15- """Find out if a particular event is configured with a default sound"""
16- try :
17- # Ask the mixer API for the number of devices it knows about.
18- # When there are no devices, PlaySound will fail.
19- if ctypes .windll .winmm .mixerGetNumDevs () == 0 :
20- return False
21-
22- key = winreg .OpenKeyEx (winreg .HKEY_CURRENT_USER ,
23- "AppEvents\Schemes\Apps\.Default\{0}\.Default" .format (sound ))
24- return winreg .EnumValue (key , 0 )[1 ] != ""
25- except OSError :
26- return False
13+
14+
15+ # Unless we actually have an ear in the room, we have no idea whether a sound
16+ # actually plays, and it's incredibly flaky trying to figure out if a sound
17+ # even *should* play. Instead of guessing, just call the function and assume
18+ # it either passed or raised the RuntimeError we expect in case of failure.
19+ def sound_func (func ):
20+ @functools .wraps (func )
21+ def wrapper (* args , ** kwargs ):
22+ try :
23+ ret = func (* args , ** kwargs )
24+ except RuntimeError as e :
25+ if support .verbose :
26+ print (func .__name__ , 'failed:' , e )
27+ else :
28+ if support .verbose :
29+ print (func .__name__ , 'returned' )
30+ return ret
31+ return wrapper
32+
33+
34+ safe_Beep = sound_func (winsound .Beep )
35+ safe_MessageBeep = sound_func (winsound .MessageBeep )
36+ safe_PlaySound = sound_func (winsound .PlaySound )
37+
2738
2839class BeepTest (unittest .TestCase ):
29- # As with PlaySoundTest, incorporate the _have_soundcard() check
30- # into our test methods. If there's no audio device present,
31- # winsound.Beep returns 0 and GetLastError() returns 127, which
32- # is: ERROR_PROC_NOT_FOUND ("The specified procedure could not
33- # be found"). (FWIW, virtual/Hyper-V systems fall under this
34- # scenario as they have no sound devices whatsoever (not even
35- # a legacy Beep device).)
3640
3741 def test_errors (self ):
3842 self .assertRaises (TypeError , winsound .Beep )
3943 self .assertRaises (ValueError , winsound .Beep , 36 , 75 )
4044 self .assertRaises (ValueError , winsound .Beep , 32768 , 75 )
4145
4246 def test_extremes (self ):
43- self . _beep (37 , 75 )
44- self . _beep (32767 , 75 )
47+ safe_Beep (37 , 75 )
48+ safe_Beep (32767 , 75 )
4549
4650 def test_increasingfrequency (self ):
4751 for i in range (100 , 2000 , 100 ):
48- self ._beep (i , 75 )
49-
50- def _beep (self , * args ):
51- # these tests used to use _have_soundcard(), but it's quite
52- # possible to have a soundcard, and yet have the beep driver
53- # disabled. So basically, we have no way of knowing whether
54- # a beep should be produced or not, so currently if these
55- # tests fail we're ignoring them
56- #
57- # XXX the right fix for this is to define something like
58- # _have_enabled_beep_driver() and use that instead of the
59- # try/except below
60- try :
61- winsound .Beep (* args )
62- except RuntimeError :
63- pass
52+ safe_Beep (i , 75 )
6453
6554class MessageBeepTest (unittest .TestCase ):
6655
@@ -70,22 +59,22 @@ def tearDown(self):
7059 def test_default (self ):
7160 self .assertRaises (TypeError , winsound .MessageBeep , "bad" )
7261 self .assertRaises (TypeError , winsound .MessageBeep , 42 , 42 )
73- winsound . MessageBeep ()
62+ safe_MessageBeep ()
7463
7564 def test_ok (self ):
76- winsound . MessageBeep (winsound .MB_OK )
65+ safe_MessageBeep (winsound .MB_OK )
7766
7867 def test_asterisk (self ):
79- winsound . MessageBeep (winsound .MB_ICONASTERISK )
68+ safe_MessageBeep (winsound .MB_ICONASTERISK )
8069
8170 def test_exclamation (self ):
82- winsound . MessageBeep (winsound .MB_ICONEXCLAMATION )
71+ safe_MessageBeep (winsound .MB_ICONEXCLAMATION )
8372
8473 def test_hand (self ):
85- winsound . MessageBeep (winsound .MB_ICONHAND )
74+ safe_MessageBeep (winsound .MB_ICONHAND )
8675
8776 def test_question (self ):
88- winsound . MessageBeep (winsound .MB_ICONQUESTION )
77+ safe_MessageBeep (winsound .MB_ICONQUESTION )
8978
9079
9180class PlaySoundTest (unittest .TestCase ):
@@ -99,151 +88,34 @@ def test_errors(self):
9988 "none" , winsound .SND_ASYNC | winsound .SND_MEMORY
10089 )
10190
102- @unittest .skipUnless (has_sound ("SystemAsterisk" ),
103- "No default SystemAsterisk" )
104- def test_alias_asterisk (self ):
105- if _have_soundcard ():
106- winsound .PlaySound ('SystemAsterisk' , winsound .SND_ALIAS )
107- else :
108- self .assertRaises (
109- RuntimeError ,
110- winsound .PlaySound ,
111- 'SystemAsterisk' , winsound .SND_ALIAS
112- )
113-
114- @unittest .skipUnless (has_sound ("SystemExclamation" ),
115- "No default SystemExclamation" )
116- def test_alias_exclamation (self ):
117- if _have_soundcard ():
118- winsound .PlaySound ('SystemExclamation' , winsound .SND_ALIAS )
119- else :
120- self .assertRaises (
121- RuntimeError ,
122- winsound .PlaySound ,
123- 'SystemExclamation' , winsound .SND_ALIAS
124- )
125-
126- @unittest .skipUnless (has_sound ("SystemExit" ), "No default SystemExit" )
127- def test_alias_exit (self ):
128- if _have_soundcard ():
129- winsound .PlaySound ('SystemExit' , winsound .SND_ALIAS )
130- else :
131- self .assertRaises (
132- RuntimeError ,
133- winsound .PlaySound ,
134- 'SystemExit' , winsound .SND_ALIAS
135- )
136-
137- @unittest .skipUnless (has_sound ("SystemHand" ), "No default SystemHand" )
138- def test_alias_hand (self ):
139- if _have_soundcard ():
140- winsound .PlaySound ('SystemHand' , winsound .SND_ALIAS )
141- else :
142- self .assertRaises (
143- RuntimeError ,
144- winsound .PlaySound ,
145- 'SystemHand' , winsound .SND_ALIAS
146- )
147-
148- @unittest .skipUnless (has_sound ("SystemQuestion" ),
149- "No default SystemQuestion" )
150- def test_alias_question (self ):
151- if _have_soundcard ():
152- winsound .PlaySound ('SystemQuestion' , winsound .SND_ALIAS )
153- else :
154- self .assertRaises (
155- RuntimeError ,
156- winsound .PlaySound ,
157- 'SystemQuestion' , winsound .SND_ALIAS
158- )
91+ def test_aliases (self ):
92+ aliases = [
93+ "SystemAsterisk" ,
94+ "SystemExclamation" ,
95+ "SystemExit" ,
96+ "SystemHand" ,
97+ "SystemQuestion" ,
98+ ]
99+ for alias in aliases :
100+ with self .subTest (alias = alias ):
101+ safe_PlaySound (alias , winsound .SND_ALIAS )
159102
160103 def test_alias_fallback (self ):
161- # In the absence of the ability to tell if a sound was actually
162- # played, this test has two acceptable outcomes: success (no error,
163- # sound was theoretically played; although as issue #19987 shows
164- # a box without a soundcard can "succeed") or RuntimeError. Any
165- # other error is a failure.
166- try :
167- winsound .PlaySound ('!"$%&/(#+*' , winsound .SND_ALIAS )
168- except RuntimeError :
169- pass
104+ safe_PlaySound ('!"$%&/(#+*' , winsound .SND_ALIAS )
170105
171106 def test_alias_nofallback (self ):
172- if _have_soundcard ():
173- # Note that this is not the same as asserting RuntimeError
174- # will get raised: you cannot convert this to
175- # self.assertRaises(...) form. The attempt may or may not
176- # raise RuntimeError, but it shouldn't raise anything other
177- # than RuntimeError, and that's all we're trying to test
178- # here. The MS docs aren't clear about whether the SDK
179- # PlaySound() with SND_ALIAS and SND_NODEFAULT will return
180- # True or False when the alias is unknown. On Tim's WinXP
181- # box today, it returns True (no exception is raised). What
182- # we'd really like to test is that no sound is played, but
183- # that requires first wiring an eardrum class into unittest
184- # <wink>.
185- try :
186- winsound .PlaySound (
187- '!"$%&/(#+*' ,
188- winsound .SND_ALIAS | winsound .SND_NODEFAULT
189- )
190- except RuntimeError :
191- pass
192- else :
193- self .assertRaises (
194- RuntimeError ,
195- winsound .PlaySound ,
196- '!"$%&/(#+*' , winsound .SND_ALIAS | winsound .SND_NODEFAULT
197- )
107+ safe_PlaySound ('!"$%&/(#+*' , winsound .SND_ALIAS | winsound .SND_NODEFAULT )
198108
199109 def test_stopasync (self ):
200- if _have_soundcard ():
201- winsound .PlaySound (
202- 'SystemQuestion' ,
203- winsound .SND_ALIAS | winsound .SND_ASYNC | winsound .SND_LOOP
204- )
205- time .sleep (0.5 )
206- try :
207- winsound .PlaySound (
208- 'SystemQuestion' ,
209- winsound .SND_ALIAS | winsound .SND_NOSTOP
210- )
211- except RuntimeError :
212- pass
213- else : # the first sound might already be finished
214- pass
215- winsound .PlaySound (None , winsound .SND_PURGE )
216- else :
217- # Issue 8367: PlaySound(None, winsound.SND_PURGE)
218- # does not raise on systems without a sound card.
219- pass
220-
221-
222- def _get_cscript_path ():
223- """Return the full path to cscript.exe or None."""
224- for dir in os .environ .get ("PATH" , "" ).split (os .pathsep ):
225- cscript_path = os .path .join (dir , "cscript.exe" )
226- if os .path .exists (cscript_path ):
227- return cscript_path
228-
229- __have_soundcard_cache = None
230- def _have_soundcard ():
231- """Return True iff this computer has a soundcard."""
232- global __have_soundcard_cache
233- if __have_soundcard_cache is None :
234- cscript_path = _get_cscript_path ()
235- if cscript_path is None :
236- # Could not find cscript.exe to run our VBScript helper. Default
237- # to True: most computers these days *do* have a soundcard.
238- return True
239-
240- check_script = os .path .join (os .path .dirname (__file__ ),
241- "check_soundcard.vbs" )
242- p = subprocess .Popen ([cscript_path , check_script ],
243- stdout = subprocess .PIPE )
244- __have_soundcard_cache = not p .wait ()
245- p .stdout .close ()
246- return __have_soundcard_cache
110+ safe_PlaySound (
111+ 'SystemQuestion' ,
112+ winsound .SND_ALIAS | winsound .SND_ASYNC | winsound .SND_LOOP
113+ )
114+ time .sleep (0.5 )
115+ safe_PlaySound ('SystemQuestion' , winsound .SND_ALIAS | winsound .SND_NOSTOP )
116+ # Issue 8367: PlaySound(None, winsound.SND_PURGE)
117+ # does not raise on systems without a sound card.
118+ winsound .PlaySound (None , winsound .SND_PURGE )
247119
248120
249121if __name__ == "__main__" :
0 commit comments