]> jfr.im git - yt-dlp.git/blame - test/test_download.py
Merge branch 'generate-ie-list'
[yt-dlp.git] / test / test_download.py
CommitLineData
fd5ff020
FV
1#!/usr/bin/env python
2
5c892b0b 3import errno
efe8902f 4import hashlib
fd5ff020 5import io
efe8902f 6import os
7f60b5aa 7import json
cdab8aa3
PH
8import unittest
9import sys
6b3aef80 10import socket
78d3442b 11import binascii
fd5ff020
FV
12
13# Allow direct execution
14sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
cdab8aa3 15
8222d8de 16import youtube_dl.YoutubeDL
d1cade5a 17import youtube_dl.extractor
fd5ff020 18from youtube_dl.utils import *
1535ac2a 19
fd5ff020
FV
20PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "parameters.json")
21
8cc83b8d
FV
22RETRIES = 3
23
fd5ff020
FV
24# General configuration (from __init__, not very elegant...)
25jar = compat_cookiejar.CookieJar()
26cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
27proxy_handler = compat_urllib_request.ProxyHandler()
28opener = compat_urllib_request.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
29compat_urllib_request.install_opener(opener)
d8bbf201 30socket.setdefaulttimeout(10)
fd5ff020 31
5c892b0b
PH
32def _try_rm(filename):
33 """ Remove a file if it exists """
34 try:
35 os.remove(filename)
36 except OSError as ose:
37 if ose.errno != errno.ENOENT:
38 raise
39
ee55fcbe 40md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest()
78d3442b 41
8222d8de 42class YoutubeDL(youtube_dl.YoutubeDL):
fd5ff020 43 def __init__(self, *args, **kwargs):
fd5ff020 44 self.to_stderr = self.to_screen
0eaf520d 45 self.processed_info_dicts = []
8222d8de 46 super(YoutubeDL, self).__init__(*args, **kwargs)
476203d0 47 def report_warning(self, message):
be95cac1
FV
48 # Don't accept warnings during tests
49 raise ExtractorError(message)
0eaf520d
FV
50 def process_info(self, info_dict):
51 self.processed_info_dicts.append(info_dict)
8222d8de 52 return super(YoutubeDL, self).process_info(info_dict)
1535ac2a 53
fd5ff020
FV
54def _file_md5(fn):
55 with open(fn, 'rb') as f:
56 return hashlib.md5(f.read()).hexdigest()
57
fc2c063e
PH
58from helper import get_testcases
59defs = get_testcases()
6b47c7f2 60
fd5ff020
FV
61with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
62 parameters = json.load(pf)
1535ac2a 63
0eaf520d 64
1535ac2a 65class TestDownload(unittest.TestCase):
744435f2 66 maxDiff = None
fd5ff020
FV
67 def setUp(self):
68 self.parameters = parameters
69 self.defs = defs
70
911ee27e 71### Dynamically generate tests
5d01a647
PH
72def generator(test_case):
73
1535ac2a 74 def test_template(self):
d1cade5a 75 ie = youtube_dl.extractor.get_info_extractor(test_case['name'])
fd5ff020
FV
76 if not ie._WORKING:
77 print('Skipping: IE marked as not _WORKING')
78 return
5c892b0b 79 if 'playlist' not in test_case and not test_case['file']:
6985325e
PH
80 print('Skipping: No output file specified')
81 return
fd5ff020
FV
82 if 'skip' in test_case:
83 print('Skipping: {0}'.format(test_case['skip']))
84 return
0eaf520d 85
c073e35b
PH
86 params = self.parameters.copy()
87 params.update(test_case.get('params', {}))
0eaf520d 88
8222d8de 89 ydl = YoutubeDL(params)
d1cade5a 90 for ie in youtube_dl.extractor.gen_extractors():
8222d8de 91 ydl.add_info_extractor(ie)
bffbd5f0
PH
92 finished_hook_called = set()
93 def _hook(status):
94 if status['status'] == 'finished':
95 finished_hook_called.add(status['filename'])
8222d8de 96 ydl.fd.add_progress_hook(_hook)
5c892b0b
PH
97
98 test_cases = test_case.get('playlist', [test_case])
99 for tc in test_cases:
100 _try_rm(tc['file'])
3a648b20 101 _try_rm(tc['file'] + '.part')
5c892b0b
PH
102 _try_rm(tc['file'] + '.info.json')
103 try:
8cc83b8d
FV
104 for retry in range(1, RETRIES + 1):
105 try:
8222d8de 106 ydl.download([test_case['url']])
8cc83b8d
FV
107 except (DownloadError, ExtractorError) as err:
108 if retry == RETRIES: raise
109
110 # Check if the exception is not a network related one
90a99c1b 111 if not err.exc_info[0] in (compat_urllib_error.URLError, socket.timeout, UnavailableVideoError):
8cc83b8d
FV
112 raise
113
114 print('Retrying: {0} failed tries\n\n##########\n\n'.format(retry))
115 else:
116 break
5c892b0b
PH
117
118 for tc in test_cases:
511eda8e 119 if not test_case.get('params', {}).get('skip_download', False):
233a2296 120 self.assertTrue(os.path.exists(tc['file']), msg='Missing file ' + tc['file'])
bffbd5f0 121 self.assertTrue(tc['file'] in finished_hook_called)
5c892b0b
PH
122 self.assertTrue(os.path.exists(tc['file'] + '.info.json'))
123 if 'md5' in tc:
124 md5_for_file = _file_md5(tc['file'])
125 self.assertEqual(md5_for_file, tc['md5'])
126 with io.open(tc['file'] + '.info.json', encoding='utf-8') as infof:
127 info_dict = json.load(infof)
51ce3a75
PH
128 for (info_field, expected) in tc.get('info_dict', {}).items():
129 if isinstance(expected, compat_str) and expected.startswith('md5:'):
130 self.assertEqual(expected, 'md5:' + md5(info_dict.get(info_field)))
78d3442b 131 else:
51ce3a75
PH
132 got = info_dict.get(info_field)
133 self.assertEqual(
134 expected, got,
135 u'invalid value for field %s, expected %r, got %r' % (info_field, expected, got))
78d3442b
FV
136
137 # If checkable fields are missing from the test case, print the info_dict
ee55fcbe 138 test_info_dict = dict((key, value if not isinstance(value, compat_str) or len(value) < 250 else 'md5:' + md5(value))
78d3442b
FV
139 for key, value in info_dict.items()
140 if value and key in ('title', 'description', 'uploader', 'upload_date', 'uploader_id', 'location'))
141 if not all(key in tc.get('info_dict', {}).keys() for key in test_info_dict.keys()):
142 sys.stderr.write(u'\n"info_dict": ' + json.dumps(test_info_dict, ensure_ascii=False, indent=2) + u'\n')
143
144 # Check for the presence of mandatory fields
145 for key in ('id', 'url', 'title', 'ext'):
146 self.assertTrue(key in info_dict.keys() and info_dict[key])
5c892b0b
PH
147 finally:
148 for tc in test_cases:
149 _try_rm(tc['file'])
3a648b20 150 _try_rm(tc['file'] + '.part')
5c892b0b 151 _try_rm(tc['file'] + '.info.json')
fd5ff020 152
1535ac2a 153 return test_template
fd5ff020 154
5d01a647 155### And add them to TestDownload
f7ab6cbe 156for n, test_case in enumerate(defs):
5d01a647 157 test_method = generator(test_case)
2eb88d95
PH
158 tname = 'test_' + str(test_case['name'])
159 i = 1
160 while hasattr(TestDownload, tname):
41beccba 161 tname = 'test_' + str(test_case['name']) + '_' + str(i)
2eb88d95
PH
162 i += 1
163 test_method.__name__ = tname
fd5ff020 164 setattr(TestDownload, test_method.__name__, test_method)
5d01a647 165 del test_method
cdab8aa3
PH
166
167
168if __name__ == '__main__':
169 unittest.main()