test_name_parser.py 18.8 KB
Newer Older
echel0n's avatar
echel0n committed
1
#!/usr/bin/env python2.7
2
# Author: echel0n <[email protected]>
echel0n's avatar
echel0n committed
3
# URL: https://sickrage.ca
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#
# This file is part of SickRage.
#
# SickRage is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# SickRage is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with SickRage.  If not, see <http://www.gnu.org/licenses/>.

20
from __future__ import print_function
21 22
from __future__ import unicode_literals

23
import os.path
24
import unittest
echel0n's avatar
echel0n committed
25 26
from datetime import date

27
import sickrage
28 29
from sickrage.core.nameparser import ParseResult, NameParser, InvalidNameException, InvalidShowException
from sickrage.core.tv.show import TVShow
30
from tests import SiCKRAGETestDBCase
31

32
sickrage.app.sys_encoding = 'UTF-8'
33 34 35 36

DEBUG = VERBOSE = False

simple_test_cases = {
37
    'standard': {
38
        'Mr.Show.Name.S01E02.Source.Quality.Etc-Group': ParseResult(None, 'Mr Show Name', 1, [2],
39
                                                                    'Source.Quality.Etc', 'Group'),
40 41 42
        'Show.Name.S01E02': ParseResult(None, 'Show Name', 1, [2]),
        'Show Name - S01E02 - My Ep Name': ParseResult(None, 'Show Name', 1, [2], 'My Ep Name'),
        'Show.1.0.Name.S01.E03.My.Ep.Name-Group': ParseResult(None, 'Show 1.0 Name', 1, [3],
43 44
                                                              'My.Ep.Name',
                                                              'Group'),
45
        'Show.Name.S01E02E03.Source.Quality.Etc-Group': ParseResult(None, 'Show Name', 1, [2, 3],
46
                                                                    'Source.Quality.Etc', 'Group'),
47
        'Mr. Show Name - S01E02-03 - My Ep Name': ParseResult(None, 'Mr. Show Name', 1, [2, 3],
48
                                                              'My Ep Name'),
49 50
        'Show.Name.S01.E02.E03': ParseResult(None, 'Show Name', 1, [2, 3]),
        'Show.Name-0.2010.S01E02.Source.Quality.Etc-Group': ParseResult(None, 'Show Name-0 2010', 1,
51 52
                                                                        [2],
                                                                        'Source.Quality.Etc', 'Group'),
53 54
        'S01E02 Ep Name': ParseResult(None, None, 1, [2], 'Ep Name'),
        'Show Name - S06E01 - 2009-12-20 - Ep Name': ParseResult(None, 'Show Name', 6, [1],
55
                                                                 '2009-12-20 - Ep Name'),
56 57 58 59 60
        'Show Name - S06E01 - -30-': ParseResult(None, 'Show Name', 6, [1], '30-'),
        'Show-Name-S06E01-720p': ParseResult(None, 'Show-Name', 6, [1], '720p'),
        'Show-Name-S06E01-1080i': ParseResult(None, 'Show-Name', 6, [1], '1080i'),
        'Show.Name.S06E01.Other.WEB-DL': ParseResult(None, 'Show Name', 6, [1], 'Other.WEB-DL'),
        'Show.Name.S06E01 Some-Stuff Here': ParseResult(None, 'Show Name', 6, [1], 'Some-Stuff Here')
61 62 63
    },

    'fov': {
64
        'Show_Name.1x02.Source_Quality_Etc-Group': ParseResult(None, 'Show Name', 1, [2],
65 66
                                                               'Source_Quality_Etc',
                                                               'Group'),
67 68 69 70
        'Show Name 1x02': ParseResult(None, 'Show Name', 1, [2]),
        'Show Name 1x02 x264 Test': ParseResult(None, 'Show Name', 1, [2], 'x264 Test'),
        'Show Name - 1x02 - My Ep Name': ParseResult(None, 'Show Name', 1, [2], 'My Ep Name'),
        'Show_Name.1x02x03x04.Source_Quality_Etc-Group': ParseResult(None, 'Show Name', 1, [2, 3, 4],
71
                                                                     'Source_Quality_Etc', 'Group'),
72
        'Show Name - 1x02-03-04 - My Ep Name': ParseResult(None, 'Show Name', 1, [2, 3, 4],
73
                                                           'My Ep Name'),
74 75 76 77 78
        '1x02 Ep Name': ParseResult(None, None, 1, [2], 'Ep Name'),
        'Show-Name-1x02-720p': ParseResult(None, 'Show-Name', 1, [2], '720p'),
        'Show-Name-1x02-1080i': ParseResult(None, 'Show-Name', 1, [2], '1080i'),
        'Show Name [05x12] Ep Name': ParseResult(None, 'Show Name', 5, [12], 'Ep Name'),
        'Show.Name.1x02.WEB-DL': ParseResult(None, 'Show Name', 1, [2], 'WEB-DL')
79 80 81
    },

    'standard_repeat': {
82
        'Show.Name.S01E02.S01E03.Source.Quality.Etc-Group': ParseResult(None, 'Show Name', 1, [2, 3],
83
                                                                        'Source.Quality.Etc', 'Group'),
84 85
        'Show.Name.S01E02.S01E03': ParseResult(None, 'Show Name', 1, [2, 3]),
        'Show Name - S01E02 - S01E03 - S01E04 - Ep Name': ParseResult(None, 'Show Name', 1, [2, 3, 4],
86
                                                                      'Ep Name'),
87
        'Show.Name.S01E02.S01E03.WEB-DL': ParseResult(None, 'Show Name', 1, [2, 3], 'WEB-DL')
88 89 90
    },

    'fov_repeat': {
91
        'Show.Name.1x02.1x03.Source.Quality.Etc-Group': ParseResult(None, 'Show Name', 1, [2, 3],
92
                                                                    'Source.Quality.Etc', 'Group'),
93 94
        'Show.Name.1x02.1x03': ParseResult(None, 'Show Name', 1, [2, 3]),
        'Show Name - 1x02 - 1x03 - 1x04 - Ep Name': ParseResult(None, 'Show Name', 1, [2, 3, 4],
95
                                                                'Ep Name'),
96
        'Show.Name.1x02.1x03.WEB-DL': ParseResult(None, 'Show Name', 1, [2, 3], 'WEB-DL')
97 98 99
    },

    'bare': {
100
        'Show.Name.102.Source.Quality.Etc-Group': ParseResult(None, 'Show Name', 1, [2],
101 102
                                                              'Source.Quality.Etc',
                                                              'Group'),
103
        'show.name.2010.123.source.quality.etc-group': ParseResult(None, 'show name 2010', 1, [23],
104
                                                                   'source.quality.etc', 'group'),
105 106
        'show.name.2010.222.123.source.quality.etc-group': ParseResult(None, 'show name 2010.222', 1,
                                                                       [23],
107
                                                                       'source.quality.etc', 'group'),
108 109
        'Show.Name.102': ParseResult(None, 'Show Name', 1, [2]),
        'the.event.401.hdtv-group': ParseResult(None, 'the event', 4, [1], 'hdtv', 'group'),
110
        'show.name.2010.special.hdtv-blah': None,
111
        'show.ex-name.102.hdtv-group': ParseResult(None, 'show ex-name', 1, [2], 'hdtv', 'group'),
112 113 114
    },

    'stupid': {
115 116
        'tpz-abc102': ParseResult(None, None, 1, [2], None, 'tpz'),
        'tpz-abc.102': ParseResult(None, None, 1, [2], None, 'tpz')
117 118 119
    },

    'no_season': {
120 121 122
        'Show Name - 01 - Ep Name': ParseResult(None, 'Show Name', None, [1], 'Ep Name'),
        '01 - Ep Name': ParseResult(None, None, None, [1], 'Ep Name'),
        'Show Name - 01 - Ep Name - WEB-DL': ParseResult(None, 'Show Name', None, [1],
123
                                                         'Ep Name - WEB-DL')
124 125 126
    },

    'no_season_general': {
127
        'Show.Name.E23.Source.Quality.Etc-Group': ParseResult(None, 'Show Name', None, [23],
128
                                                              'Source.Quality.Etc', 'Group'),
129 130
        'Show Name - Episode 01 - Ep Name': ParseResult(None, 'Show Name', None, [1], 'Ep Name'),
        'Show.Name.Part.3.Source.Quality.Etc-Group': ParseResult(None, 'Show Name', None, [3],
131
                                                                 'Source.Quality.Etc', 'Group'),
132
        'Show.Name.Part.1.and.Part.2.Blah-Group': ParseResult(None, 'Show Name', None, [1, 2], 'Blah',
133
                                                              'Group'),
134
        'Show.Name.Part.IV.Source.Quality.Etc-Group': ParseResult(None, 'Show Name', None, [4],
135
                                                                  'Source.Quality.Etc', 'Group'),
136 137
        'Deconstructed.E07.1080i.HDTV.DD5.1.MPEG2-TrollHD': ParseResult(None, 'Deconstructed', None,
                                                                        [7],
138
                                                                        '1080i.HDTV.DD5.1.MPEG2', 'TrollHD'),
139
        'Show.Name.E23.WEB-DL': ParseResult(None, 'Show Name', None, [23], 'WEB-DL'),
140 141 142
    },

    'no_season_multi_ep': {
143
        'Show.Name.E23-24.Source.Quality.Etc-Group': ParseResult(None, 'Show Name', None, [23, 24],
144
                                                                 'Source.Quality.Etc', 'Group'),
145 146
        'Show Name - Episode 01-02 - Ep Name': ParseResult(None, 'Show Name', None, [1, 2], 'Ep Name'),
        'Show.Name.E23-24.WEB-DL': ParseResult(None, 'Show Name', None, [23, 24], 'WEB-DL')
147 148 149
    },

    'season_only': {
150
        'Show.Name.S02.Source.Quality.Etc-Group': ParseResult(None, 'Show Name', 2, [],
151 152
                                                              'Source.Quality.Etc',
                                                              'Group'),
153 154
        'Show Name Season 2': ParseResult(None, 'Show Name', 2),
        'Season 02': ParseResult(None, None, 2)
155 156 157
    },

    'scene_date_format': {
158
        'Show.Name.2010.11.23.Source.Quality.Etc-Group': ParseResult(None, 'Show Name', None, [],
159
                                                                     'Source.Quality.Etc', 'Group',
echel0n's avatar
echel0n committed
160 161
                                                                     date(2010, 11, 23)),
        'Show Name - 2010.11.23': ParseResult(None, 'Show Name', air_date=date(2010, 11, 23)),
162
        'Show.Name.2010.23.11.Source.Quality.Etc-Group': ParseResult(None, 'Show Name', None, [],
163 164
                                                                     'Source.Quality.Etc', 'Group',
                                                                     date(2010, 11, 23)),
165
        'Show Name - 2010-11-23 - Ep Name': ParseResult(None, 'Show Name', extra_info='Ep Name',
echel0n's avatar
echel0n committed
166
                                                        air_date=date(2010, 11, 23)),
167
        '2010-11-23 - Ep Name': ParseResult(None, extra_info='Ep Name',
echel0n's avatar
echel0n committed
168
                                            air_date=date(2010, 11, 23)),
169
        'Show.Name.2010.11.23.WEB-DL': ParseResult(None, 'Show Name', None, [], 'WEB-DL', None,
echel0n's avatar
echel0n committed
170
                                                   date(2010, 11, 23))
171 172
    },
}
173 174

combination_test_cases = [
175
    ('/test/path/to/Season 02/03 - Ep Name.avi',
176
     ParseResult(None, None, 2, [3], 'Ep Name'),
177
     ['no_season', 'season_only']),
178

179
    ('Show.Name.S02.Source.Quality.Etc-Group/tpz-sn203.avi',
180
     ParseResult(None, 'Show Name', 2, [3], 'Source.Quality.Etc', 'Group'),
181
     ['stupid', 'season_only']),
182

183
    ('MythBusters.S08E16.720p.HDTV.x264-aAF/aaf-mb.s08e16.720p.mkv',
184
     ParseResult(None, 'MythBusters', 8, [16], '720p.HDTV.x264', 'aAF'),
185
     ['standard']),
186

187
    (
188 189 190
        '/home/drop/storage/TV/Terminator The Sarah Connor Chronicles/Season 2/S02E06 The Tower is Tall, But the Fall is Short.mkv',
        ParseResult(None, None, 2, [6], 'The Tower is Tall, But the Fall is Short'),
        ['standard']),
191

192
    (r'/Test/TV/Jimmy Fallon/Season 2/Jimmy Fallon - 2010-12-15 - blah.avi',
echel0n's avatar
echel0n committed
193
     ParseResult(None, 'Jimmy Fallon', extra_info='blah', air_date=date(2010, 12, 15)),
194
     ['scene_date_format']),
195

196
    (r'/X/30 Rock/Season 4/30 Rock - 4x22 -.avi',
197
     ParseResult(None, '30 Rock', 4, [22]),
198
     ['fov']),
199

200
    ('Season 2\\Show Name - 03-04 - Ep Name.ext',
201
     ParseResult(None, 'Show Name', 2, [3, 4], extra_info='Ep Name'),
202
     ['no_season', 'season_only']),
203

204
    ('Season 02\\03-04-05 - Ep Name.ext',
205
     ParseResult(None, None, 2, [3, 4, 5], extra_info='Ep Name'),
206 207
     ['no_season', 'season_only']),
]
208 209

unicode_test_cases = [
210
    ('The.Big.Bang.Theory.2x07.The.Panty.Pi\xf1ata.Polarization.720p.HDTV.x264.AC3-SHELDON.mkv',
211
     ParseResult(None, 'The.Big.Bang.Theory', 2, [7],
212 213
                 'The.Panty.Pi\xf1ata.Polarization.720p.HDTV.x264.AC3',
                 'SHELDON')),
214
    ('The.Big.Bang.Theory.2x07.The.Panty.Pi\xc3\xb1ata.Polarization.720p.HDTV.x264.AC3-SHELDON.mkv',
215
     ParseResult(None, 'The.Big.Bang.Theory', 2, [7],
216 217
                 'The.Panty.Pi\xc3\xb1ata.Polarization.720p.HDTV.x264.AC3',
                 'SHELDON'))
218
]
219 220 221 222

failure_cases = ['7sins-jfcs01e09-720p-bluray-x264']


223
class UnicodeTests(SiCKRAGETestDBCase):
224 225
    def __init__(self, something):
        super(UnicodeTests, self).__init__(something)
echel0n's avatar
echel0n committed
226
        self.setUp()
227
        self.show = TVShow(1, 1, 'en')
228
        self.show.name = "The Big Bang Theory"
echel0n's avatar
echel0n committed
229 230
        self.show.saveToDB()
        self.show.loadFromDB(skipNFO=True)
231

232
    def _test_unicode(self, name, result):
233
        np = NameParser(True, showObj=self.show)
234
        parse_result = np.parse(name)
235

236
        # this shouldn't raise an exception
237 238
        repr(str(parse_result))
        self.assertEqual(parse_result.extra_info, result.extra_info)
239

240 241 242 243
    def test_unicode(self):
        for (name, result) in unicode_test_cases:
            self._test_unicode(name, result)

244

245
class FailureCaseTests(SiCKRAGETestDBCase):
246 247
    @staticmethod
    def _test_name(name):
248
        np = NameParser(True)
249 250
        try:
            parse_result = np.parse(name)
251
        except (InvalidNameException, InvalidShowException):
252
            return True
253

254
        if VERBOSE:
255
            print('Actual: ', parse_result.which_regex, parse_result)
256
        return False
257

258 259 260 261
    def test_failures(self):
        for name in failure_cases:
            self.assertTrue(self._test_name(name))

262

263
class ComboTests(SiCKRAGETestDBCase):
264
    def _test_combo(self, name, result, which_regexes):
265

266
        if VERBOSE:
267 268
            print()
            print('Testing', name)
269

270
        np = NameParser(True)
271 272 273

        try:
            test_result = np.parse(name)
274
        except InvalidShowException:
275
            return False
276

277
        if DEBUG:
278 279
            print(test_result, test_result.which_regex)
            print(result, which_regexes)
280

281 282 283 284 285 286
        self.assertEqual(test_result, result)
        for cur_regex in which_regexes:
            self.assertTrue(cur_regex in test_result.which_regex)
        self.assertEqual(len(which_regexes), len(test_result.which_regex))

    def test_combos(self):
287

288 289 290
        for (name, result, which_regexes) in combination_test_cases:
            # Normalise the paths. Converts UNIX-style paths into Windows-style
            # paths when test is run on Windows.
291
            self._test_combo(os.path.normpath(name), result, which_regexes)
292 293


294
class BasicTests(SiCKRAGETestDBCase):
295 296 297
    def __init__(self, something):
        super(BasicTests, self).__init__(something)
        super(BasicTests, self).setUp()
298
        self.show = TVShow(1, 1, 'en')
echel0n's avatar
echel0n committed
299
        self.show.saveToDB()
300

301 302
    def _test_names(self, np, section, transform=None, verbose=False):
        if VERBOSE or verbose:
303
            print('Running', section, 'tests')
304 305 306
        for cur_test_base in simple_test_cases[section]:
            if transform:
                cur_test = transform(cur_test_base)
307
                np.file_name = cur_test
308 309 310
            else:
                cur_test = cur_test_base
            if VERBOSE or verbose:
311
                print('Testing', cur_test)
312 313

            result = simple_test_cases[section][cur_test_base]
314 315 316

            self.show.name = result.series_name if result else None
            np.showObj = self.show
317
            if not result:
318
                self.assertRaises(InvalidNameException, np.parse, cur_test)
319 320
                return
            else:
321
                result.which_regex = [section]
322
                test_result = np.parse(cur_test)
323

324
            if DEBUG or verbose:
325 326 327 328
                print('air_by_date:', test_result.is_air_by_date, 'air_date:', test_result.air_date)
                print('anime:', test_result.is_anime, 'ab_episode_numbers:', test_result.ab_episode_numbers)
                print(test_result)
                print(result)
329
            self.assertEqual(test_result.which_regex, [section])
330 331 332
            self.assertEqual(str(test_result), str(result))

    def test_standard_names(self):
333
        np = NameParser(True)
334 335 336
        self._test_names(np, 'standard')

    def test_standard_repeat_names(self):
337
        np = NameParser(False)
338 339 340
        self._test_names(np, 'standard_repeat')

    def test_fov_names(self):
341
        np = NameParser(False)
342 343 344
        self._test_names(np, 'fov')

    def test_fov_repeat_names(self):
345
        np = NameParser(False)
346
        self._test_names(np, 'fov_repeat')
347

348
    # def test_bare_names(self):
349 350
    #    np = parser.NameParser(False)
    #    self._test_names(np, 'bare')
351 352

    def test_stupid_names(self):
353
        np = NameParser(False)
354 355
        self._test_names(np, 'stupid')

356
    # def test_no_season_names(self):
357 358
    #    np = parser.NameParser(False)
    #    self._test_names(np, 'no_season')
359 360

    def test_no_season_general_names(self):
361
        np = NameParser(False)
362 363 364
        self._test_names(np, 'no_season_general')

    def test_no_season_multi_ep_names(self):
365
        np = NameParser(False)
366 367 368
        self._test_names(np, 'no_season_multi_ep')

    def test_season_only_names(self):
369
        np = NameParser(False)
370 371
        self._test_names(np, 'season_only')

372
    # def test_scene_date_format_names(self):
373 374
    #    np = parser.NameParser(False)
    #    self._test_names(np, 'scene_date_format')
375 376

    def test_standard_file_names(self):
377
        np = NameParser()
378 379 380
        self._test_names(np, 'standard', lambda x: x + '.avi')

    def test_standard_repeat_file_names(self):
381
        np = NameParser()
382 383 384
        self._test_names(np, 'standard_repeat', lambda x: x + '.avi')

    def test_fov_file_names(self):
385
        np = NameParser()
386 387 388
        self._test_names(np, 'fov', lambda x: x + '.avi')

    def test_fov_repeat_file_names(self):
389
        np = NameParser()
390 391
        self._test_names(np, 'fov_repeat', lambda x: x + '.avi')

392
    # def test_bare_file_names(self):
393 394
    #    np = parser.NameParser()
    #    self._test_names(np, 'bare', lambda x: x + '.avi')
395 396

    def test_stupid_file_names(self):
397
        np = NameParser()
398 399
        self._test_names(np, 'stupid', lambda x: x + '.avi')

400
    # def test_no_season_file_names(self):
401 402
    #    np = parser.NameParser()
    #    self._test_names(np, 'no_season', lambda x: x + '.avi')
403 404

    def test_no_season_general_file_names(self):
405
        np = NameParser()
406 407 408
        self._test_names(np, 'no_season_general', lambda x: x + '.avi')

    def test_no_season_multi_ep_file_names(self):
409
        np = NameParser()
410 411 412
        self._test_names(np, 'no_season_multi_ep', lambda x: x + '.avi')

    def test_season_only_file_names(self):
413
        np = NameParser()
414 415
        self._test_names(np, 'season_only', lambda x: x + '.avi')

416
    # def test_scene_date_format_file_names(self):
417 418 419
    #    np = parser.NameParser()
    #    self._test_names(np, 'scene_date_format', lambda x: x + '.avi')

420 421 422
    def test_combination_names(self):
        pass

423

424
if __name__ == '__main__':
425 426 427 428
    print("==================")
    print("STARTING - NAME PARSER TESTS")
    print("==================")
    print("######################################################################")
429
    unittest.main()