1
1
# Ridiculously simple test of the winsound module for Windows.
2
2
3
- import unittest
4
- from test import support
5
- support .requires ('audio' )
6
- import time
3
+ import functools
7
4
import os
8
5
import subprocess
6
+ import time
7
+ import unittest
8
+
9
+ from test import support
9
10
11
+ support .requires ('audio' )
10
12
winsound = 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
+
27
38
28
39
class 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).)
36
40
37
41
def test_errors (self ):
38
42
self .assertRaises (TypeError , winsound .Beep )
39
43
self .assertRaises (ValueError , winsound .Beep , 36 , 75 )
40
44
self .assertRaises (ValueError , winsound .Beep , 32768 , 75 )
41
45
42
46
def test_extremes (self ):
43
- self . _beep (37 , 75 )
44
- self . _beep (32767 , 75 )
47
+ safe_Beep (37 , 75 )
48
+ safe_Beep (32767 , 75 )
45
49
46
50
def test_increasingfrequency (self ):
47
51
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 )
64
53
65
54
class MessageBeepTest (unittest .TestCase ):
66
55
@@ -70,22 +59,22 @@ def tearDown(self):
70
59
def test_default (self ):
71
60
self .assertRaises (TypeError , winsound .MessageBeep , "bad" )
72
61
self .assertRaises (TypeError , winsound .MessageBeep , 42 , 42 )
73
- winsound . MessageBeep ()
62
+ safe_MessageBeep ()
74
63
75
64
def test_ok (self ):
76
- winsound . MessageBeep (winsound .MB_OK )
65
+ safe_MessageBeep (winsound .MB_OK )
77
66
78
67
def test_asterisk (self ):
79
- winsound . MessageBeep (winsound .MB_ICONASTERISK )
68
+ safe_MessageBeep (winsound .MB_ICONASTERISK )
80
69
81
70
def test_exclamation (self ):
82
- winsound . MessageBeep (winsound .MB_ICONEXCLAMATION )
71
+ safe_MessageBeep (winsound .MB_ICONEXCLAMATION )
83
72
84
73
def test_hand (self ):
85
- winsound . MessageBeep (winsound .MB_ICONHAND )
74
+ safe_MessageBeep (winsound .MB_ICONHAND )
86
75
87
76
def test_question (self ):
88
- winsound . MessageBeep (winsound .MB_ICONQUESTION )
77
+ safe_MessageBeep (winsound .MB_ICONQUESTION )
89
78
90
79
91
80
class PlaySoundTest (unittest .TestCase ):
@@ -99,151 +88,34 @@ def test_errors(self):
99
88
"none" , winsound .SND_ASYNC | winsound .SND_MEMORY
100
89
)
101
90
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 )
159
102
160
103
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 )
170
105
171
106
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 )
198
108
199
109
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 )
247
119
248
120
249
121
if __name__ == "__main__" :
0 commit comments