1 # Ridiculously simple test of the winsound module for Windows. 2 3 import unittest 4 from test import test_support 5 test_support.requires('audio') 6 import time 7 import os 8 import subprocess 9 10 winsound = test_support.import_module('winsound') 11 ctypes = test_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() is 0: 20 return False 21 22 key = _winreg.OpenKeyEx(_winreg.HKEY_CURRENT_USER, 23 "AppEvents\Schemes\Apps\.Default\{0}\.Default".format(sound)) 24 value = _winreg.EnumValue(key, 0)[1] 25 if value is not u"": 26 return True 27 else: 28 return False 29 except WindowsError: 30 return False 31 32 class BeepTest(unittest.TestCase): 33 # As with PlaySoundTest, incorporate the _have_soundcard() check 34 # into our test methods. If there's no audio device present, 35 # winsound.Beep returns 0 and GetLastError() returns 127, which 36 # is: ERROR_PROC_NOT_FOUND ("The specified procedure could not 37 # be found"). (FWIW, virtual/Hyper-V systems fall under this 38 # scenario as they have no sound devices whatsoever (not even 39 # a legacy Beep device).) 40 41 def test_errors(self): 42 self.assertRaises(TypeError, winsound.Beep) 43 self.assertRaises(ValueError, winsound.Beep, 36, 75) 44 self.assertRaises(ValueError, winsound.Beep, 32768, 75) 45 46 def test_extremes(self): 47 self._beep(37, 75) 48 self._beep(32767, 75) 49 50 def test_increasingfrequency(self): 51 for i in xrange(100, 2000, 100): 52 self._beep(i, 75) 53 54 def _beep(self, *args): 55 # these tests used to use _have_soundcard(), but it's quite 56 # possible to have a soundcard, and yet have the beep driver 57 # disabled. So basically, we have no way of knowing whether 58 # a beep should be produced or not, so currently if these 59 # tests fail we're ignoring them 60 # 61 # XXX the right fix for this is to define something like 62 # _have_enabled_beep_driver() and use that instead of the 63 # try/except below 64 try: 65 winsound.Beep(*args) 66 except RuntimeError: 67 pass 68 69 class MessageBeepTest(unittest.TestCase): 70 71 def tearDown(self): 72 time.sleep(0.5) 73 74 def test_default(self): 75 self.assertRaises(TypeError, winsound.MessageBeep, "bad") 76 self.assertRaises(TypeError, winsound.MessageBeep, 42, 42) 77 winsound.MessageBeep() 78 79 def test_ok(self): 80 winsound.MessageBeep(winsound.MB_OK) 81 82 def test_asterisk(self): 83 winsound.MessageBeep(winsound.MB_ICONASTERISK) 84 85 def test_exclamation(self): 86 winsound.MessageBeep(winsound.MB_ICONEXCLAMATION) 87 88 def test_hand(self): 89 winsound.MessageBeep(winsound.MB_ICONHAND) 90 91 def test_question(self): 92 winsound.MessageBeep(winsound.MB_ICONQUESTION) 93 94 95 class PlaySoundTest(unittest.TestCase): 96 97 def test_errors(self): 98 self.assertRaises(TypeError, winsound.PlaySound) 99 self.assertRaises(TypeError, winsound.PlaySound, "bad", "bad") 100 self.assertRaises( 101 RuntimeError, 102 winsound.PlaySound, 103 "none", winsound.SND_ASYNC | winsound.SND_MEMORY 104 ) 105 106 @unittest.skipUnless(has_sound("SystemAsterisk"), "No default SystemAsterisk") 107 def test_alias_asterisk(self): 108 if _have_soundcard(): 109 winsound.PlaySound('SystemAsterisk', winsound.SND_ALIAS) 110 else: 111 self.assertRaises( 112 RuntimeError, 113 winsound.PlaySound, 114 'SystemAsterisk', winsound.SND_ALIAS 115 ) 116 117 @unittest.skipUnless(has_sound("SystemExclamation"), "No default SystemExclamation") 118 def test_alias_exclamation(self): 119 if _have_soundcard(): 120 winsound.PlaySound('SystemExclamation', winsound.SND_ALIAS) 121 else: 122 self.assertRaises( 123 RuntimeError, 124 winsound.PlaySound, 125 'SystemExclamation', winsound.SND_ALIAS 126 ) 127 128 @unittest.skipUnless(has_sound("SystemExit"), "No default SystemExit") 129 def test_alias_exit(self): 130 if _have_soundcard(): 131 winsound.PlaySound('SystemExit', winsound.SND_ALIAS) 132 else: 133 self.assertRaises( 134 RuntimeError, 135 winsound.PlaySound, 136 'SystemExit', winsound.SND_ALIAS 137 ) 138 139 @unittest.skipUnless(has_sound("SystemHand"), "No default SystemHand") 140 def test_alias_hand(self): 141 if _have_soundcard(): 142 winsound.PlaySound('SystemHand', winsound.SND_ALIAS) 143 else: 144 self.assertRaises( 145 RuntimeError, 146 winsound.PlaySound, 147 'SystemHand', winsound.SND_ALIAS 148 ) 149 150 @unittest.skipUnless(has_sound("SystemQuestion"), "No default SystemQuestion") 151 def test_alias_question(self): 152 if _have_soundcard(): 153 winsound.PlaySound('SystemQuestion', winsound.SND_ALIAS) 154 else: 155 self.assertRaises( 156 RuntimeError, 157 winsound.PlaySound, 158 'SystemQuestion', winsound.SND_ALIAS 159 ) 160 161 def test_alias_fallback(self): 162 # This test can't be expected to work on all systems. The MS 163 # PlaySound() docs say: 164 # 165 # If it cannot find the specified sound, PlaySound uses the 166 # default system event sound entry instead. If the function 167 # can find neither the system default entry nor the default 168 # sound, it makes no sound and returns FALSE. 169 # 170 # It's known to return FALSE on some real systems. 171 172 # winsound.PlaySound('!"$%&/(#+*', winsound.SND_ALIAS) 173 return 174 175 def test_alias_nofallback(self): 176 if _have_soundcard(): 177 # Note that this is not the same as asserting RuntimeError 178 # will get raised: you cannot convert this to 179 # self.assertRaises(...) form. The attempt may or may not 180 # raise RuntimeError, but it shouldn't raise anything other 181 # than RuntimeError, and that's all we're trying to test 182 # here. The MS docs aren't clear about whether the SDK 183 # PlaySound() with SND_ALIAS and SND_NODEFAULT will return 184 # True or False when the alias is unknown. On Tim's WinXP 185 # box today, it returns True (no exception is raised). What 186 # we'd really like to test is that no sound is played, but 187 # that requires first wiring an eardrum class into unittest 188 # <wink>. 189 try: 190 winsound.PlaySound( 191 '!"$%&/(#+*', 192 winsound.SND_ALIAS | winsound.SND_NODEFAULT 193 ) 194 except RuntimeError: 195 pass 196 else: 197 self.assertRaises( 198 RuntimeError, 199 winsound.PlaySound, 200 '!"$%&/(#+*', winsound.SND_ALIAS | winsound.SND_NODEFAULT 201 ) 202 203 def test_stopasync(self): 204 if _have_soundcard(): 205 winsound.PlaySound( 206 'SystemQuestion', 207 winsound.SND_ALIAS | winsound.SND_ASYNC | winsound.SND_LOOP 208 ) 209 time.sleep(0.5) 210 try: 211 winsound.PlaySound( 212 'SystemQuestion', 213 winsound.SND_ALIAS | winsound.SND_NOSTOP 214 ) 215 except RuntimeError: 216 pass 217 else: # the first sound might already be finished 218 pass 219 winsound.PlaySound(None, winsound.SND_PURGE) 220 else: 221 # Issue 8367: PlaySound(None, winsound.SND_PURGE) 222 # does not raise on systems without a sound card. 223 pass 224 225 226 def _get_cscript_path(): 227 """Return the full path to cscript.exe or None.""" 228 for dir in os.environ.get("PATH", "").split(os.pathsep): 229 cscript_path = os.path.join(dir, "cscript.exe") 230 if os.path.exists(cscript_path): 231 return cscript_path 232 233 __have_soundcard_cache = None 234 def _have_soundcard(): 235 """Return True iff this computer has a soundcard.""" 236 global __have_soundcard_cache 237 if __have_soundcard_cache is None: 238 cscript_path = _get_cscript_path() 239 if cscript_path is None: 240 # Could not find cscript.exe to run our VBScript helper. Default 241 # to True: most computers these days *do* have a soundcard. 242 return True 243 244 check_script = os.path.join(os.path.dirname(__file__), 245 "check_soundcard.vbs") 246 p = subprocess.Popen([cscript_path, check_script], 247 stdout=subprocess.PIPE) 248 __have_soundcard_cache = not p.wait() 249 return __have_soundcard_cache 250 251 252 def test_main(): 253 test_support.run_unittest(BeepTest, MessageBeepTest, PlaySoundTest) 254 255 if __name__=="__main__": 256 test_main() 257