]> jfr.im git - yt-dlp.git/blame - test/test_config.py
[ie/TubiTv] Fix extractor (#9975)
[yt-dlp.git] / test / test_config.py
CommitLineData
773c272d
SS
1#!/usr/bin/env python3
2
3# Allow direct execution
4import os
5import sys
6import unittest
7import unittest.mock
8
9sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
10
11import contextlib
12import itertools
13from pathlib import Path
14
15from yt_dlp.compat import compat_expanduser
16from yt_dlp.options import create_parser, parseOpts
17from yt_dlp.utils import Config, get_executable_path
18
19ENVIRON_DEFAULTS = {
20 'HOME': None,
21 'XDG_CONFIG_HOME': '/_xdg_config_home/',
22 'USERPROFILE': 'C:/Users/testing/',
23 'APPDATA': 'C:/Users/testing/AppData/Roaming/',
24 'HOMEDRIVE': 'C:/',
25 'HOMEPATH': 'Users/testing/',
26}
27
28
29@contextlib.contextmanager
30def set_environ(**kwargs):
31 saved_environ = os.environ.copy()
32
33 for name, value in {**ENVIRON_DEFAULTS, **kwargs}.items():
34 if value is None:
35 os.environ.pop(name, None)
36 else:
37 os.environ[name] = value
38
39 yield
40
41 os.environ.clear()
42 os.environ.update(saved_environ)
43
44
45def _generate_expected_groups():
46 xdg_config_home = os.getenv('XDG_CONFIG_HOME') or compat_expanduser('~/.config')
47 appdata_dir = os.getenv('appdata')
48 home_dir = compat_expanduser('~')
49 return {
50 'Portable': [
51 Path(get_executable_path(), 'yt-dlp.conf'),
52 ],
53 'Home': [
54 Path('yt-dlp.conf'),
55 ],
56 'User': [
57 Path(xdg_config_home, 'yt-dlp.conf'),
58 Path(xdg_config_home, 'yt-dlp', 'config'),
59 Path(xdg_config_home, 'yt-dlp', 'config.txt'),
60 *((
61 Path(appdata_dir, 'yt-dlp.conf'),
62 Path(appdata_dir, 'yt-dlp', 'config'),
63 Path(appdata_dir, 'yt-dlp', 'config.txt'),
64 ) if appdata_dir else ()),
65 Path(home_dir, 'yt-dlp.conf'),
66 Path(home_dir, 'yt-dlp.conf.txt'),
67 Path(home_dir, '.yt-dlp', 'config'),
68 Path(home_dir, '.yt-dlp', 'config.txt'),
69 ],
70 'System': [
71 Path('/etc/yt-dlp.conf'),
72 Path('/etc/yt-dlp/config'),
73 Path('/etc/yt-dlp/config.txt'),
74 ]
75 }
76
77
78class TestConfig(unittest.TestCase):
79 maxDiff = None
80
81 @set_environ()
82 def test_config__ENVIRON_DEFAULTS_sanity(self):
83 expected = make_expected()
84 self.assertCountEqual(
85 set(expected), expected,
86 'ENVIRON_DEFAULTS produces non unique names')
87
88 def test_config_all_environ_values(self):
89 for name, value in ENVIRON_DEFAULTS.items():
90 for new_value in (None, '', '.', value or '/some/dir'):
91 with set_environ(**{name: new_value}):
92 self._simple_grouping_test()
93
94 def test_config_default_expected_locations(self):
95 files, _ = self._simple_config_test()
96 self.assertEqual(
97 files, make_expected(),
98 'Not all expected locations have been checked')
99
100 def test_config_default_grouping(self):
101 self._simple_grouping_test()
102
103 def _simple_grouping_test(self):
104 expected_groups = make_expected_groups()
105 for name, group in expected_groups.items():
106 for index, existing_path in enumerate(group):
107 result, opts = self._simple_config_test(existing_path)
108 expected = expected_from_expected_groups(expected_groups, existing_path)
109 self.assertEqual(
110 result, expected,
111 f'The checked locations do not match the expected ({name}, {index})')
112 self.assertEqual(
113 opts.outtmpl['default'], '1',
114 f'The used result value was incorrect ({name}, {index})')
115
116 def _simple_config_test(self, *stop_paths):
117 encountered = 0
118 paths = []
119
120 def read_file(filename, default=[]):
121 nonlocal encountered
122 path = Path(filename)
123 paths.append(path)
124 if path in stop_paths:
125 encountered += 1
126 return ['-o', f'{encountered}']
127
128 with ConfigMock(read_file):
129 _, opts, _ = parseOpts([], False)
130
131 return paths, opts
132
133 @set_environ()
134 def test_config_early_exit_commandline(self):
135 self._early_exit_test(0, '--ignore-config')
136
137 @set_environ()
138 def test_config_early_exit_files(self):
139 for index, _ in enumerate(make_expected(), 1):
140 self._early_exit_test(index)
141
142 def _early_exit_test(self, allowed_reads, *args):
143 reads = 0
144
145 def read_file(filename, default=[]):
146 nonlocal reads
147 reads += 1
148
149 if reads > allowed_reads:
150 self.fail('The remaining config was not ignored')
151 elif reads == allowed_reads:
152 return ['--ignore-config']
153
154 with ConfigMock(read_file):
155 parseOpts(args, False)
156
157 @set_environ()
158 def test_config_override_commandline(self):
159 self._override_test(0, '-o', 'pass')
160
161 @set_environ()
162 def test_config_override_files(self):
163 for index, _ in enumerate(make_expected(), 1):
164 self._override_test(index)
165
166 def _override_test(self, start_index, *args):
167 index = 0
168
169 def read_file(filename, default=[]):
170 nonlocal index
171 index += 1
172
173 if index > start_index:
174 return ['-o', 'fail']
175 elif index == start_index:
176 return ['-o', 'pass']
177
178 with ConfigMock(read_file):
179 _, opts, _ = parseOpts(args, False)
180
181 self.assertEqual(
182 opts.outtmpl['default'], 'pass',
183 'The earlier group did not override the later ones')
184
185
186@contextlib.contextmanager
187def ConfigMock(read_file=None):
188 with unittest.mock.patch('yt_dlp.options.Config') as mock:
189 mock.return_value = Config(create_parser())
190 if read_file is not None:
191 mock.read_file = read_file
192
193 yield mock
194
195
196def make_expected(*filepaths):
197 return expected_from_expected_groups(_generate_expected_groups(), *filepaths)
198
199
200def make_expected_groups(*filepaths):
201 return _filter_expected_groups(_generate_expected_groups(), filepaths)
202
203
204def expected_from_expected_groups(expected_groups, *filepaths):
205 return list(itertools.chain.from_iterable(
206 _filter_expected_groups(expected_groups, filepaths).values()))
207
208
209def _filter_expected_groups(expected, filepaths):
210 if not filepaths:
211 return expected
212
213 result = {}
214 for group, paths in expected.items():
215 new_paths = []
216 for path in paths:
217 new_paths.append(path)
218 if path in filepaths:
219 break
220
221 result[group] = new_paths
222
223 return result
224
225
226if __name__ == '__main__':
227 unittest.main()