@@ -295,6 +295,145 @@ def local_withauth(localserver):
295295 yield req
296296
297297
298+ def test_slow_download_warning (monkeypatch , assert_log ):
299+ now = [10.0 ]
300+ monkeypatch .setattr (UU .time , "monotonic" , lambda : now [0 ])
301+
302+ def urlopen (request ):
303+ request .on_progress (0 )
304+ now [0 ] += 5
305+ request .on_progress (50 )
306+ assert_log (assert_log .not_logged ("Downloading .+ is taking some time.+" ))
307+
308+ now [0 ] += 0.1
309+ request .on_progress (50 )
310+ request .on_progress (75 )
311+ return b"download"
312+
313+ monkeypatch .setattr (UU , "ENABLE_WINHTTP" , True )
314+ monkeypatch .setattr (UU , "_winhttp_urlopen" , urlopen )
315+
316+ result = UU .urlopen ("https://user:pass@example.com/index.json" )
317+
318+ assert result == b"download"
319+ assert_log (
320+ (
321+ "Downloading %s is taking some time. Please continue to wait, "
322+ "or press Ctrl\\ +C to abort." ,
323+ ["https://example.com/index.json" ],
324+ ),
325+ assert_log .end_of_log (),
326+ )
327+
328+
329+ def test_slow_download_warning_not_emitted_on_completion (monkeypatch , assert_log ):
330+ now = [10.0 ]
331+ monkeypatch .setattr (UU .time , "monotonic" , lambda : now [0 ])
332+ request = UU ._Request ("https://example.com/index.json" )
333+
334+ now [0 ] += 5.1
335+ request .on_progress (100 )
336+
337+ assert_log (assert_log .not_logged ("Downloading .+ is taking some time.+" ))
338+
339+
340+ def test_slow_download_warning_suppressed_by_progress (monkeypatch , assert_log ):
341+ now = [10.0 ]
342+ monkeypatch .setattr (UU .time , "monotonic" , lambda : now [0 ])
343+ request = UU ._Request ("https://example.com/index.json" )
344+ progress = []
345+ request ._on_progress = progress .append
346+
347+ now [0 ] += 5.1
348+ request .on_progress (50 )
349+
350+ assert progress == [50 ]
351+ assert_log (assert_log .not_logged ("Downloading .+ is taking some time.+" ))
352+
353+
354+ def test_powershell_slow_download_warning (monkeypatch , assert_log , tmp_path ):
355+ import shutil
356+ import subprocess
357+
358+ now = [10.0 ]
359+ monkeypatch .setattr (UU .time , "monotonic" , lambda : now [0 ])
360+ monkeypatch .setattr (shutil , "which" , lambda _ : "powershell.exe" )
361+
362+ class Process :
363+ def __init__ (self , * args , ** kwargs ):
364+ pass
365+
366+ def __enter__ (self ):
367+ return self
368+
369+ def __exit__ (self , * exc_info ):
370+ pass
371+
372+ def communicate (self , _ = None , timeout = None ):
373+ if timeout is None :
374+ return (b"" ,)
375+ now [0 ] += timeout
376+ if now [0 ] <= 16 :
377+ raise subprocess .TimeoutExpired ("powershell.exe" , timeout )
378+ return (b"" ,)
379+
380+ def terminate (self ):
381+ pass
382+
383+ monkeypatch .setattr (subprocess , "Popen" , Process )
384+
385+ request = UU ._Request ("https://user:pass@example.com/index.json" )
386+ request .outfile = tmp_path / "index.json"
387+
388+ UU ._powershell_urlretrieve (request )
389+
390+ warning = (
391+ "Downloading %s is taking some time. Please continue to wait, "
392+ "or press Ctrl+C to abort."
393+ )
394+ assert_log (assert_log .skip_until (
395+ warning .replace ("+" , "\\ +" ),
396+ ["https://example.com/index.json" ],
397+ ))
398+ assert sum (1 for msg , _ in assert_log if msg == warning ) == 1
399+
400+
401+ def test_bits_slow_download_warning (monkeypatch , assert_log , tmp_path ):
402+ bits = object ()
403+ job = object ()
404+ now = [10.0 ]
405+ progress = iter ([0 ] * 52 + [100 ])
406+
407+ def sleep (delay ):
408+ now [0 ] += delay
409+
410+ monkeypatch .setattr (UU .time , "monotonic" , lambda : now [0 ])
411+ monkeypatch .setattr (UU .time , "sleep" , sleep )
412+ monkeypatch .setattr (_native , "coinitialize" , lambda : None , raising = False )
413+ monkeypatch .setattr (_native , "bits_connect" , lambda : bits , raising = False )
414+ monkeypatch .setattr (_native , "bits_begin" , lambda * a , ** k : job , raising = False )
415+ monkeypatch .setattr (_native , "bits_cancel" , lambda * a : None , raising = False )
416+ monkeypatch .setattr (_native , "bits_get_progress" , lambda * a : next (progress ), raising = False )
417+ monkeypatch .setattr (_native , "bits_retry_with_auth" , lambda * a : None , raising = False )
418+ monkeypatch .setattr (_native , "bits_find_job" , lambda * a : None , raising = False )
419+ monkeypatch .setattr (_native , "bits_serialize_job" , lambda * a : b"job-id" , raising = False )
420+
421+ request = UU ._Request ("https://user:pass@example.com/download.zip" )
422+ request .outfile = tmp_path / "download.zip"
423+
424+ UU ._bits_urlretrieve (request )
425+
426+ warning = (
427+ "Downloading %s is taking some time. Please continue to wait, "
428+ "or press Ctrl+C to abort."
429+ )
430+ assert_log (assert_log .skip_until (
431+ warning .replace ("+" , "\\ +" ),
432+ ["https://example.com/download.zip" ],
433+ ))
434+ assert sum (1 for msg , _ in assert_log if msg == warning ) == 1
435+
436+
298437def test_urllib_urlretrieve (local_128kb , tmp_path ):
299438 local_128kb .outfile = dest = tmp_path / "read.txt"
300439 progress = local_128kb .progress
0 commit comments