Skip to content

Commit b3b7a5a

Browse files
committed
Issue #27748: Simplify test_winsound.
The tests no longer attempt to figure out if a soundcard or particular system sounds are available. Instead, it just tries everything and accepts RuntimeError as a flavor of success.
1 parent f7e2ea2 commit b3b7a5a

File tree

2 files changed

+62
-203
lines changed

2 files changed

+62
-203
lines changed

Lib/test/check_soundcard.vbs

Lines changed: 0 additions & 13 deletions
This file was deleted.

Lib/test/test_winsound.py

Lines changed: 62 additions & 190 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,55 @@
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
74
import os
85
import subprocess
6+
import time
7+
import unittest
8+
9+
from test import support
910

11+
support.requires('audio')
1012
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+
2738

2839
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).)
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

6554
class 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

9180
class 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

249121
if __name__ == "__main__":

0 commit comments

Comments
 (0)