]> jfr.im git - yt-dlp.git/blame - test/helper.py
[youtube] Skip unsupported adaptive stream type (#18804)
[yt-dlp.git] / test / helper.py
CommitLineData
7a08ad7d
PH
1from __future__ import unicode_literals
2
f4aac741 3import errno
112da0a0 4import io
44a5f171 5import hashlib
112da0a0
PH
6import json
7import os.path
00fcc17a
FV
8import re
9import types
95e42d73 10import ssl
dd508b7c 11import sys
112da0a0 12
fc2c063e 13import youtube_dl.extractor
44a5f171 14from youtube_dl import YoutubeDL
e9c0cdd3
YCH
15from youtube_dl.compat import (
16 compat_os_name,
257cfebf 17 compat_str,
e9c0cdd3
YCH
18)
19from youtube_dl.utils import (
257cfebf 20 preferredencoding,
c0f64ac6 21 write_string,
257cfebf 22)
112da0a0 23
112da0a0 24
44a5f171
PH
25def get_params(override=None):
26 PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)),
27 "parameters.json")
72f3289a
YCH
28 LOCAL_PARAMETERS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)),
29 "local_parameters.json")
44a5f171
PH
30 with io.open(PARAMETERS_FILE, encoding='utf-8') as pf:
31 parameters = json.load(pf)
72f3289a
YCH
32 if os.path.exists(LOCAL_PARAMETERS_FILE):
33 with io.open(LOCAL_PARAMETERS_FILE, encoding='utf-8') as pf:
34 parameters.update(json.load(pf))
44a5f171
PH
35 if override:
36 parameters.update(override)
37 return parameters
112da0a0 38
f4aac741
PH
39
40def try_rm(filename):
41 """ Remove a file if it exists """
42 try:
43 os.remove(filename)
44 except OSError as ose:
45 if ose.errno != errno.ENOENT:
46 raise
47
48
dd508b7c
FV
49def report_warning(message):
50 '''
51 Print the message to stderr, it will be prefixed with 'WARNING:'
52 If stderr is a tty file the 'WARNING:' will be colored
53 '''
e9c0cdd3 54 if sys.stderr.isatty() and compat_os_name != 'nt':
7a08ad7d 55 _msg_header = '\033[0;33mWARNING:\033[0m'
dd508b7c 56 else:
7a08ad7d
PH
57 _msg_header = 'WARNING:'
58 output = '%s %s\n' % (_msg_header, message)
dd508b7c
FV
59 if 'b' in getattr(sys.stderr, 'mode', '') or sys.version_info[0] < 3:
60 output = output.encode(preferredencoding())
61 sys.stderr.write(output)
62
63
112da0a0 64class FakeYDL(YoutubeDL):
f4d96df0 65 def __init__(self, override=None):
112da0a0
PH
66 # Different instances of the downloader can't share the same dictionary
67 # some test set the "sublang" parameter, which would break the md5 checks.
f4d96df0 68 params = get_params(override=override)
ac35c266 69 super(FakeYDL, self).__init__(params, auto_init=False)
f4aac741 70 self.result = []
5f6a1245 71
f4aac741 72 def to_screen(self, s, skip_eol=None):
112da0a0 73 print(s)
f4aac741 74
112da0a0
PH
75 def trouble(self, s, tb=None):
76 raise Exception(s)
f4aac741 77
112da0a0 78 def download(self, x):
fc2c063e 79 self.result.append(x)
f4aac741 80
00fcc17a
FV
81 def expect_warning(self, regex):
82 # Silence an expected warning matching a regex
83 old_report_warning = self.report_warning
5f6a1245 84
00fcc17a 85 def report_warning(self, message):
5f6a1245
JW
86 if re.match(regex, message):
87 return
00fcc17a
FV
88 old_report_warning(message)
89 self.report_warning = types.MethodType(report_warning, self)
fc2c063e 90
52fadd5f
PH
91
92def gettestcases(include_onlymatching=False):
fc2c063e 93 for ie in youtube_dl.extractor.gen_extractors():
05900629
PH
94 for tc in ie.get_testcases(include_onlymatching):
95 yield tc
44a5f171
PH
96
97
98md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest()
257cfebf
PH
99
100
40c931de
QF
101def expect_value(self, got, expected, field):
102 if isinstance(expected, compat_str) and expected.startswith('re:'):
103 match_str = expected[len('re:'):]
104 match_rex = re.compile(match_str)
105
106 self.assertTrue(
107 isinstance(got, compat_str),
108 'Expected a %s object, but got %s for field %s' % (
109 compat_str.__name__, type(got).__name__, field))
110 self.assertTrue(
111 match_rex.match(got),
112 'field %s (value: %r) should match %r' % (field, got, match_str))
113 elif isinstance(expected, compat_str) and expected.startswith('startswith:'):
114 start_str = expected[len('startswith:'):]
115 self.assertTrue(
116 isinstance(got, compat_str),
117 'Expected a %s object, but got %s for field %s' % (
118 compat_str.__name__, type(got).__name__, field))
119 self.assertTrue(
120 got.startswith(start_str),
121 'field %s (value: %r) should start with %r' % (field, got, start_str))
122 elif isinstance(expected, compat_str) and expected.startswith('contains:'):
123 contains_str = expected[len('contains:'):]
124 self.assertTrue(
125 isinstance(got, compat_str),
126 'Expected a %s object, but got %s for field %s' % (
127 compat_str.__name__, type(got).__name__, field))
128 self.assertTrue(
129 contains_str in got,
130 'field %s (value: %r) should contain %r' % (field, got, contains_str))
131 elif isinstance(expected, type):
2e885de7
S
132 self.assertTrue(
133 isinstance(got, expected),
134 'Expected type %r for field %s, but got value %r of type %r' % (expected, field, got, type(got)))
40c931de
QF
135 elif isinstance(expected, dict) and isinstance(got, dict):
136 expect_dict(self, got, expected)
137 elif isinstance(expected, list) and isinstance(got, list):
2e885de7
S
138 self.assertEqual(
139 len(expected), len(got),
f88f1b40
S
140 'Expect a list of length %d, but got a list of length %d for field %s' % (
141 len(expected), len(got), field))
687c04cb
QF
142 for index, (item_got, item_expected) in enumerate(zip(got, expected)):
143 type_got = type(item_got)
144 type_expected = type(item_expected)
2e885de7
S
145 self.assertEqual(
146 type_expected, type_got,
386a7b52 147 'Type mismatch for list item at index %d for field %s, expected %r, got %r' % (
7d0ada5f 148 index, field, type_expected, type_got))
687c04cb 149 expect_value(self, item_got, item_expected, field)
40c931de
QF
150 else:
151 if isinstance(expected, compat_str) and expected.startswith('md5:'):
6c4c7539
YCH
152 self.assertTrue(
153 isinstance(got, compat_str),
154 'Expected field %s to be a unicode object, but got value %r of type %r' % (field, got, type(got)))
40c931de
QF
155 got = 'md5:' + md5(got)
156 elif isinstance(expected, compat_str) and expected.startswith('mincount:'):
9789d753 157 self.assertTrue(
40c931de
QF
158 isinstance(got, (list, dict)),
159 'Expected field %s to be a list or a dict, but it is of type %s' % (
160 field, type(got).__name__))
161 expected_num = int(expected.partition(':')[2])
162 assertGreaterEqual(
163 self, len(got), expected_num,
2e885de7 164 'Expected %d items in field %s, but only got %d' % (expected_num, field, len(got)))
40c931de 165 return
2e885de7
S
166 self.assertEqual(
167 expected, got,
386a7b52 168 'Invalid value for field %s, expected %r, got %r' % (field, expected, got))
40c931de
QF
169
170
171def expect_dict(self, got_dict, expected_dict):
172 for info_field, expected in expected_dict.items():
173 got = got_dict.get(info_field)
174 expect_value(self, got, expected, info_field)
257cfebf 175
93bc7ef1
QF
176
177def expect_info_dict(self, got_dict, expected_dict):
178 expect_dict(self, got_dict, expected_dict)
ea38e55f 179 # Check for the presence of mandatory fields
880ee801 180 if got_dict.get('_type') not in ('playlist', 'multi_video'):
e8ee972c
PH
181 for key in ('id', 'url', 'title', 'ext'):
182 self.assertTrue(got_dict.get(key), 'Missing mandatory field %s' % key)
ea38e55f
PH
183 # Check for mandatory fields that are automatically set by YoutubeDL
184 for key in ['webpage_url', 'extractor', 'extractor_key']:
7a08ad7d 185 self.assertTrue(got_dict.get(key), 'Missing field: %s' % key)
ea38e55f
PH
186
187 # Are checkable fields missing from the test case definition?
188 test_info_dict = dict((key, value if not isinstance(value, compat_str) or len(value) < 250 else 'md5:' + md5(value))
9e1a5b84 189 for key, value in got_dict.items()
8e2b1be1 190 if value and key in ('id', 'title', 'description', 'uploader', 'upload_date', 'timestamp', 'uploader_id', 'location', 'age_limit'))
ea38e55f
PH
191 missing_keys = set(test_info_dict.keys()) - set(expected_dict.keys())
192 if missing_keys:
c0f64ac6
PH
193 def _repr(v):
194 if isinstance(v, compat_str):
155f9550 195 return "'%s'" % v.replace('\\', '\\\\').replace("'", "\\'").replace('\n', '\\n')
c0f64ac6
PH
196 else:
197 return repr(v)
dc35bfd2
PH
198 info_dict_str = ''
199 if len(missing_keys) != len(expected_dict):
200 info_dict_str += ''.join(
201 ' %s: %s,\n' % (_repr(k), _repr(v))
202 for k, v in test_info_dict.items() if k not in missing_keys)
6f53c63d
PH
203
204 if info_dict_str:
205 info_dict_str += '\n'
dc35bfd2
PH
206 info_dict_str += ''.join(
207 ' %s: %s,\n' % (_repr(k), _repr(test_info_dict[k]))
208 for k in missing_keys)
3e6e4999 209 write_string(
f21e915f 210 '\n\'info_dict\': {\n' + info_dict_str + '},\n', out=sys.stderr)
ea38e55f
PH
211 self.assertFalse(
212 missing_keys,
213 'Missing keys in test definition: %s' % (
214 ', '.join(sorted(missing_keys))))
c57f7757
PH
215
216
217def assertRegexpMatches(self, text, regexp, msg=None):
0fd7fd71
PH
218 if hasattr(self, 'assertRegexp'):
219 return self.assertRegexp(text, regexp, msg)
c57f7757
PH
220 else:
221 m = re.match(regexp, text)
222 if not m:
8bdcb436
PH
223 note = 'Regexp didn\'t match: %r not found' % (regexp)
224 if len(text) < 1000:
225 note += ' in %r' % text
c57f7757
PH
226 if msg is None:
227 msg = note
228 else:
229 msg = note + ', ' + msg
230 self.assertTrue(m, msg)
d8624e6a
PH
231
232
233def assertGreaterEqual(self, got, expected, msg=None):
234 if not (got >= expected):
235 if msg is None:
236 msg = '%r not greater than or equal to %r' % (got, expected)
237 self.assertTrue(got >= expected, msg)
70b7e3fb
PH
238
239
240def expect_warnings(ydl, warnings_re):
241 real_warning = ydl.report_warning
242
243 def _report_warning(w):
244 if not any(re.search(w_re, w) for w_re in warnings_re):
245 real_warning(w)
246
247 ydl.report_warning = _report_warning
95e42d73
XDG
248
249
250def http_server_port(httpd):
251 if os.name == 'java' and isinstance(httpd.socket, ssl.SSLSocket):
252 # In Jython SSLSocket is not a subclass of socket.socket
253 sock = httpd.socket.sock
254 else:
255 sock = httpd.socket
256 return sock.getsockname()[1]