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