From 7c06806bc4600f06226281166a99e6677bb754c3 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 10 Dec 2023 17:24:43 -0500 Subject: [PATCH] test: add initial unit tests and code cleanup (#129) --- .github/workflows/CI.yml | 76 +++++++++++++++++-- .gitignore | 3 + Contents/Code/__init__.py | 23 ++---- Contents/Services/URL/YouTube/ServiceCode.pys | 21 ++--- README.rst | 4 + codecov.yml | 15 ++++ docs/Makefile | 2 +- docs/make.bat | 5 +- docs/source/contributing/testing.rst | 4 +- patches/youtube_dl-compat.patch | 38 ++++++++++ patches/youtube_dl-extractor.patch | 25 ++++++ requirements-dev.txt | 3 +- requirements.txt | 6 +- tests/__init__.py | 0 tests/conftest.py | 16 ++++ tests/functional/__init__.py | 0 tests/functional/test_docs.py | 72 ++++++++++++++++++ tests/unit/__init__.py | 0 tests/unit/test_code.py | 43 +++++++++++ tests/unit/url_services/__init__.py | 0 tests/unit/url_services/conftest.py | 25 ++++++ tests/unit/url_services/test_youtube.py | 56 ++++++++++++++ 22 files changed, 388 insertions(+), 49 deletions(-) create mode 100644 codecov.yml create mode 100644 patches/youtube_dl-compat.patch create mode 100644 patches/youtube_dl-extractor.patch create mode 100644 tests/__init__.py create mode 100644 tests/conftest.py create mode 100644 tests/functional/__init__.py create mode 100644 tests/functional/test_docs.py create mode 100644 tests/unit/__init__.py create mode 100644 tests/unit/test_code.py create mode 100644 tests/unit/url_services/__init__.py create mode 100644 tests/unit/url_services/conftest.py create mode 100644 tests/unit/url_services/test_youtube.py diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 161d4cf..cba646b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -42,6 +42,7 @@ jobs: uses: LizardByte/setup-release-action@v2023.1207.150459 with: github_token: ${{ secrets.GITHUB_TOKEN }} + build: needs: - setup_release @@ -72,6 +73,14 @@ jobs: python -m pip install --upgrade --target=./Contents/Libraries/Shared -r \ requirements.txt --no-warn-script-location + - name: Patch python deps + shell: bash + working-directory: PlexyGlass.bundle/Contents/Libraries/Shared + run: | + patch_dir=${{ github.workspace }}/PlexyGlass.bundle/patches + patch -p1 < "${patch_dir}/youtube_dl-compat.patch" + patch -p1 < "${patch_dir}/youtube_dl-extractor.patch" + - name: Build plist working-directory: PlexyGlass.bundle env: @@ -79,13 +88,6 @@ jobs: run: | python ./scripts/build_plist.py - - name: Test Plex Plugin - # todo - replace with pytest - working-directory: PlexyGlass.bundle - run: | - python ./Contents/Code/__init__.py - python ./Contents/Services/URL/YouTube/ServiceCode.pys - - name: Package Release shell: bash run: | @@ -125,3 +127,63 @@ jobs: prerelease: ${{ needs.setup_release.outputs.publish_pre_release }} tag: ${{ needs.setup_release.outputs.release_tag }} token: ${{ secrets.GH_BOT_TOKEN }} + + pytest: + needs: [build] + strategy: + fail-fast: false + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download artifacts + uses: actions/download-artifact@v3 + with: + name: PlexyGlass.bundle + + - name: Extract artifacts zip + shell: bash + run: | + # extract zip + 7z x PlexyGlass.bundle.zip -o. + + # move all files from "PlexyGlass.bundle" to root, with no target directory + cp -r ./PlexyGlass.bundle/. . + + # remove zip + rm PlexyGlass.bundle.zip + + - name: Set up Python + uses: LizardByte/setup-python-action@v2023.1210.35516 + with: + python-version: '2.7' + + - name: Install python dependencies + shell: bash + run: | + python -m pip --no-python-version-warning --disable-pip-version-check install --upgrade \ + pip setuptools wheel + python -m pip --no-python-version-warning --disable-pip-version-check install -r requirements-dev.txt + + - name: Test with pytest + id: test + shell: bash + run: | + python -m pytest \ + -rxXs \ + --tb=native \ + --verbose \ + --cov=Contents/Code \ + --cov=Contents/Services \ + tests + + - name: Upload coverage + # any except canceled or skipped + if: always() && (steps.test.outcome == 'success' || steps.test.outcome == 'failure') + uses: codecov/codecov-action@v3 + with: + flags: ${{ runner.os }} diff --git a/.gitignore b/.gitignore index da38023..6a2e17b 100644 --- a/.gitignore +++ b/.gitignore @@ -168,3 +168,6 @@ plexhints-temp # Remove python modules Contents/Libraries/Shared/ + +# Remove plex service file cache +*.pys[cod] diff --git a/Contents/Code/__init__.py b/Contents/Code/__init__.py index f30c034..bad24aa 100644 --- a/Contents/Code/__init__.py +++ b/Contents/Code/__init__.py @@ -1,8 +1,5 @@ # -*- coding: utf-8 -*- -# standard imports -import sys - # plex debugging try: import plexhints # noqa: F401 @@ -19,13 +16,10 @@ from plexhints.prefs_kit import Prefs # prefs kit # local imports -if sys.version_info.major < 3: - from default_prefs import default_prefs -else: - from .default_prefs import default_prefs +from default_prefs import default_prefs -def validate_prefs(): +def ValidatePrefs(): # type: () -> MessageContainer """ Validate plug-in preferences. @@ -44,7 +38,7 @@ def validate_prefs(): Examples -------- - >>> validate_prefs() + >>> ValidatePrefs() ... """ # todo - validate values are proper type of data, same as retroarcher @@ -79,7 +73,7 @@ def validate_prefs(): return MessageContainer(header='Success', message='RetroArcher - Provided preference values are ok') -def start(): +def Start(): # type: () -> None """ Start the plug-in. @@ -92,11 +86,11 @@ def start(): Examples -------- - >>> start() + >>> Start() ... """ # validate prefs - prefs_valid = validate_prefs() + prefs_valid = ValidatePrefs() if prefs_valid.header == 'Error': Log.Warn('PlexyGlass plug-in preferences are not valid.') @@ -112,8 +106,3 @@ def main(): and since Plex removed menu's from plug-ins, this method does not need to perform any other function. """ pass - - -# remap plex predefined functions... function names should be lowercase -Start = start -ValidatePrefs = validate_prefs diff --git a/Contents/Services/URL/YouTube/ServiceCode.pys b/Contents/Services/URL/YouTube/ServiceCode.pys index 34bb1ea..763668e 100644 --- a/Contents/Services/URL/YouTube/ServiceCode.pys +++ b/Contents/Services/URL/YouTube/ServiceCode.pys @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # standard imports @@ -104,8 +103,6 @@ def extract_youtube_data(url): Log.Error('%s :: %s %s Service :: error: %s' % (plugin_name, service_name, service_type, e)) raise Ex.MediaNotAvailable except youtube_dl.utils.DownloadError as e: - print('----------') - print(e) if 'Sign in to confirm your age' in str(e): Log.Error('%s :: %s %s Service :: error: %s' % (plugin_name, service_name, service_type, e)) raise Ex.MediaNotAuthorized @@ -125,7 +122,7 @@ def extract_youtube_data(url): return -def normalize_url(url): +def NormalizeURL(url): # type: (str) -> Optional[str] """ Get the video webpage url from `youtube-dl`. @@ -142,7 +139,7 @@ def normalize_url(url): Examples -------- - >>> normalize_url(url='https://www.youtube.com/watch?v=dQw4w9WgXcQ') + >>> NormalizeURL(url='https://www.youtube.com/watch?v=dQw4w9WgXcQ') 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' """ Log.Info('%s :: %s %s Service :: normalizing url: %s' % (plugin_name, service_name, service_type, url)) @@ -162,7 +159,7 @@ def normalize_url(url): return webpage_url -def metadata_object_for_url(url): +def MetadataObjectForURL(url): # type: (str) -> Optional[VideoClipObject] """ Get YouTube metadata for a given URL. @@ -179,7 +176,7 @@ def metadata_object_for_url(url): Examples -------- - >>> metadata_object_for_url(url='https://www.youtube.com/watch?v=dQw4w9WgXcQ') + >>> MetadataObjectForURL(url='https://www.youtube.com/watch?v=dQw4w9WgXcQ') ... """ Log.Info('%s :: %s %s Service :: collecting metadata for url: %s' % (plugin_name, service_name, service_type, url)) @@ -247,7 +244,7 @@ def metadata_object_for_url(url): ) -def media_objects_for_url(url): +def MediaObjectsForURL(url): # type: (str) -> Optional[list] """ Build the Plex media objects for a given URL. @@ -264,7 +261,7 @@ def media_objects_for_url(url): Examples -------- - >>> media_objects_for_url(url='https://www.youtube.com/watch?v=dQw4w9WgXcQ') + >>> MediaObjectsForURL(url='https://www.youtube.com/watch?v=dQw4w9WgXcQ') [...] """ Log.Info('%s :: %s %s Service :: attempting to create media object for url: %s' % ( @@ -339,9 +336,3 @@ def play_video(url=None, default_fmt=None, **kwargs): return IndirectResponse(VideoClipObject, key=final_url) return - - -# remap plex predefined functions... function names should be lowercase -NormalizeURL = normalize_url -MetadataObjectForURL = metadata_object_for_url -MediaObjectsForURL = media_objects_for_url diff --git a/README.rst b/README.rst index 2a67c0b..e3c08cc 100644 --- a/README.rst +++ b/README.rst @@ -20,6 +20,10 @@ Integrations :alt: Read the Docs :target: http://plexyglass.readthedocs.io/ +.. image:: https://img.shields.io/codecov/c/gh/LizardByte/PlexyGlass?token=X8WDZVM33W&style=for-the-badge&logo=codecov&label=codecov + :alt: Codecov + :target: https://codecov.io/gh/LizardByte/PlexyGlass + Downloads --------- diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..c9d3a1a --- /dev/null +++ b/codecov.yml @@ -0,0 +1,15 @@ +--- +codecov: + branch: master + +coverage: + status: + project: + default: + target: auto + threshold: 10% + +comment: + layout: "diff, flags, files" + behavior: default + require_changes: false # if true: only post the comment if coverage changes diff --git a/docs/Makefile b/docs/Makefile index d0c3cbf..8b6275a 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -3,7 +3,7 @@ # You can set these variables from the command line, and also # from the environment for the first two. -SPHINXOPTS ?= +SPHINXOPTS ?= -W --keep-going SPHINXBUILD ?= sphinx-build SOURCEDIR = source BUILDDIR = build diff --git a/docs/make.bat b/docs/make.bat index dc1312a..08ca223 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -9,6 +9,7 @@ if "%SPHINXBUILD%" == "" ( ) set SOURCEDIR=source set BUILDDIR=build +set "SPHINXOPTS=-W --keep-going" %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( @@ -25,11 +26,11 @@ if errorlevel 9009 ( if "%1" == "" goto help -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% || exit /b %ERRORLEVEL% goto end :help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% || exit /b %ERRORLEVEL% :end popd diff --git a/docs/source/contributing/testing.rst b/docs/source/contributing/testing.rst index 672c910..0d3f8af 100644 --- a/docs/source/contributing/testing.rst +++ b/docs/source/contributing/testing.rst @@ -1,4 +1,4 @@ -:github_url: https://github.com/RetroArcher/RetroArcher.bundle/blob/master/docs/source/contributing/testing.rst +:github_url: https://github.com/LizardByte/PlexyGlass/blob/master/docs/source/contributing/testing.rst Testing ======= @@ -41,8 +41,6 @@ Test with Sphinx pytest ------ -.. Todo:: PyTest is not yet implemented. - PlexyGlass uses `pytest `__ for unit testing. pytest is included in the ``requirements-dev.txt``. diff --git a/patches/youtube_dl-compat.patch b/patches/youtube_dl-compat.patch new file mode 100644 index 0000000..9c87839 --- /dev/null +++ b/patches/youtube_dl-compat.patch @@ -0,0 +1,38 @@ +diff --git a/youtube_dl/compat.py b/youtube_dl/compat.py +index 3c526a78d..6e2a92d92 100644 +--- a/youtube_dl/compat.py ++++ b/youtube_dl/compat.py +@@ -58,18 +58,22 @@ except ImportError: # Python 2 + + # Also fix up lack of method arg in old Pythons + try: +- _req = compat_urllib_request.Request +- _req('http://127.0.0.1', method='GET') ++ type(compat_urllib_request.Request('http://127.0.0.1', method='GET')) + except TypeError: +- class _request(object): +- def __new__(cls, url, *args, **kwargs): +- method = kwargs.pop('method', None) +- r = _req(url, *args, **kwargs) +- if method: +- r.get_method = types.MethodType(lambda _: method, r) +- return r +- +- compat_urllib_request.Request = _request ++ def _add_init_method_arg(cls): ++ init = cls.__init__ ++ ++ def wrapped_init(self, *args, **kwargs): ++ method = kwargs.pop('method', 'GET') ++ init(self, *args, **kwargs) ++ if self.has_data() and method == 'GET': ++ method = 'POST' ++ self.get_method = types.MethodType(lambda _: method, self) ++ ++ cls.__init__ = wrapped_init ++ ++ _add_init_method_arg(compat_urllib_request.Request) ++ del _add_init_method_arg + + + try: diff --git a/patches/youtube_dl-extractor.patch b/patches/youtube_dl-extractor.patch new file mode 100644 index 0000000..384e6fd --- /dev/null +++ b/patches/youtube_dl-extractor.patch @@ -0,0 +1,25 @@ +diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py +index 9c419c002..3bf483c1c 100644 +--- a/youtube_dl/extractor/youtube.py ++++ b/youtube_dl/extractor/youtube.py +@@ -260,16 +260,10 @@ class YoutubeBaseInfoExtractor(InfoExtractor): + cookies = self._get_cookies('https://www.youtube.com/') + if cookies.get('__Secure-3PSID'): + return +- consent_id = None +- consent = cookies.get('CONSENT') +- if consent: +- if 'YES' in consent.value: +- return +- consent_id = self._search_regex( +- r'PENDING\+(\d+)', consent.value, 'consent', default=None) +- if not consent_id: +- consent_id = random.randint(100, 999) +- self._set_cookie('.youtube.com', 'CONSENT', 'YES+cb.20210328-17-p0.en+FX+%s' % consent_id) ++ socs = cookies.get('SOCS') ++ if socs and not socs.value.startswith('CAA'): # not consented ++ return ++ self._set_cookie('.youtube.com', 'SOCS', 'CAI', secure=True) # accept all (required for mixes) + + def _real_initialize(self): + self._initialize_consent() diff --git a/requirements-dev.txt b/requirements-dev.txt index 9294be6..e2e1b73 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,8 +3,9 @@ flake8==3.9.2;python_version<"3" m2r2==0.3.2;python_version<"3" numpydoc==0.9.2;python_version<"3" pathlib2==2.3.7.post1;python_version<"3" -git+https://github.com/LizardByte/plexhints.git#egg=plexhints # type hinting library for plex development +plexhints==2023.1210.163618 # type hinting library for plex development pytest==4.6.11;python_version<"3" +pytest-cov==2.12.1;python_version<"3" rstcheck==3.5.0;python_version<"3" Sphinx==1.8.6;python_version<"3" sphinx-rtd-theme==1.1.1;python_version<"3" diff --git a/requirements.txt b/requirements.txt index e1423d8..6c06689 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ typing==3.10.0.0 # youtube_dl is not capable or willing to create a new release so have to install from git # youtube_dl==2021.12.17 -# unknown if dependabot can update this -# git+https://github.com/ytdl-org/youtube-dl.git@26035bde46c0acc30dc053618451d9aeca4b7709#egg=youtube_dl +# dependabot cannot update this +# git+https://github.com/ytdl-org/youtube-dl.git@00ef748cc0e35ee60efd0f7a00e373ab8d1af86b#egg=youtube_dl # do not use git as git in ubuntu 22.04 does not work with python 2.7 -https://github.com/ytdl-org/youtube-dl/archive/26035bde46c0acc30dc053618451d9aeca4b7709.zip#egg=youtube_dl +https://github.com/ytdl-org/youtube-dl/archive/00ef748cc0e35ee60efd0f7a00e373ab8d1af86b.zip#egg=youtube_dl diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..4db58ab --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +# standard imports +import os +import sys + +# lib imports +import pytest + +# add Contents directory to the system path +pytest.root_dir = root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +pytest.contents_dir = contents_dir = os.path.join(root_dir, 'Contents') +if os.path.isdir(contents_dir): + sys.path.append(contents_dir) +else: + raise Exception('Contents directory not found') diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/functional/test_docs.py b/tests/functional/test_docs.py new file mode 100644 index 0000000..8e119c7 --- /dev/null +++ b/tests/functional/test_docs.py @@ -0,0 +1,72 @@ +import os +import platform +import pytest +import shutil +import subprocess + + +def build_docs(): + """Test building sphinx docs""" + doc_types = [ + 'html', + 'epub', + ] + + # remove existing build directory + build_dir = os.path.join(os.getcwd(), 'docs', 'build') + if os.path.isdir(build_dir): + shutil.rmtree(path=build_dir) + + for doc_type in doc_types: + print('Building {} docs'.format(doc_type)) + result = subprocess.check_call( + args=['make', doc_type], + cwd=os.path.join(os.getcwd(), 'docs'), + shell=True if platform.system() == 'Windows' else False, + ) + assert result == 0, 'Failed to build {} docs'.format(doc_type) + + # ensure docs built + assert os.path.isfile(os.path.join(build_dir, 'html', 'index.html')), 'HTML docs not built' + assert os.path.isfile(os.path.join(build_dir, 'epub', 'PlexyGlass.epub')), 'EPUB docs not built' + + +def test_make_docs(): + """Test building working sphinx docs""" + build_docs() + + +def test_make_docs_broken(): + """Test building sphinx docs with known warnings""" + # create a dummy rst file + dummy_file = os.path.join(os.getcwd(), 'docs', 'source', 'dummy.rst') + + # write test to dummy file, creating the file if it doesn't exist + with open(dummy_file, 'w+') as f: + f.write('Dummy file\n') + f.write('==========\n') + + # ensure CalledProcessError is raised + with pytest.raises(subprocess.CalledProcessError): + build_docs() + + # remove the dummy rst file + os.remove(dummy_file) + + +def test_rstcheck(): + """Test rstcheck""" + # get list of all the rst files in the project (skip venv and Contents/Libraries) + rst_files = [] + for root, dirs, files in os.walk(os.getcwd()): + for f in files: + if f.lower().endswith('.rst') and 'venv' not in root and 'Contents/Libraries' not in root: + rst_files.append(os.path.join(root, f)) + + assert rst_files, 'No rst files found' + + # run rstcheck on all the rst files + for rst_file in rst_files: + print('Checking {}'.format(rst_file)) + result = subprocess.check_call(['rstcheck', rst_file]) + assert result == 0, 'rstcheck failed on {}'.format(rst_file) diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/test_code.py b/tests/unit/test_code.py new file mode 100644 index 0000000..93eaad9 --- /dev/null +++ b/tests/unit/test_code.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +# lib imports +from plexhints.object_kit import MessageContainer + +# local imports +from Code import Start +from Code import ValidatePrefs +from Code import default_prefs + + +def test_validate_prefs(): + result_container = ValidatePrefs() + assert isinstance(result_container, MessageContainer) + assert result_container.header == "Success" + assert "Provided preference values are ok" in result_container.message + + # invalidate prefs, cannot do this due to: + # TypeError: '_PreferenceSet' object does not support item assignment + # Code.Prefs['int_plexapi_plexapi_timeout'] = 'not an integer' + # result_container = ValidatePrefs() + # assert isinstance(result_container, MessageContainer) + # assert result_container.header == "Error" + # assert "must be an integer" in result_container.message + + +def test_validate_prefs_default_prefs(): + # add a default pref and make sure it is not in DefaultPrefs.json + default_prefs['new_pref'] = 'new_value' + result_container = ValidatePrefs() + assert isinstance(result_container, MessageContainer) + assert result_container.header == "Error" + assert "missing from 'DefaultPrefs.json'" in result_container.message + + +def test_start(): + # just run the function + Start() + + +def test_main(): + # todo + pass diff --git a/tests/unit/url_services/__init__.py b/tests/unit/url_services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/url_services/conftest.py b/tests/unit/url_services/conftest.py new file mode 100644 index 0000000..563d057 --- /dev/null +++ b/tests/unit/url_services/conftest.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- + +# standard imports +import imp +import os + +# lib imports +import pytest + +# get Services directories +services_dir = url_services_dir = os.path.join(pytest.contents_dir, 'Services') +url_dir = os.path.join(services_dir, 'URL') + +service_file = 'ServiceCode.pys' + + +@pytest.fixture(scope='module') +def service_url_youtube(): + # type: () -> object + + # we need to use imp.load_source() to import the service code because it is not a standard python module + name = 'YouTube' + _name = 'service_url_{}'.format(name.lower()) + service = imp.load_source(_name, os.path.join(url_dir, name, service_file)) + return service diff --git a/tests/unit/url_services/test_youtube.py b/tests/unit/url_services/test_youtube.py new file mode 100644 index 0000000..4b645db --- /dev/null +++ b/tests/unit/url_services/test_youtube.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +# lib imports +import pytest + + +@pytest.fixture(scope='module') +def service(service_url_youtube): + yield service_url_youtube + + +@pytest.fixture(scope='module', params=[ + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + 'https://youtube.com/watch?v=dQw4w9WgXcQ', + 'https://youtu.be/dQw4w9WgXcQ', +]) +def url_list_1(request): + yield request.param + + +# only use this for normalize because the playlist takes a long time to process +@pytest.fixture(scope='module', params=[ + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ', + 'https://youtube.com/watch?v=dQw4w9WgXcQ', + 'https://youtu.be/dQw4w9WgXcQ', + 'https://www.youtube.com/watch?v=dQw4w9WgXcQ&list=RDdQw4w9WgXcQ&start_radio=1' # mix +]) +def url_list_2(request): + yield request.param + + +def test_extract_youtube_data(service, url_list_1): + assert service.extract_youtube_data(url=url_list_1) + + +def test_normalize_url(service, url_list_2): + uut = service.NormalizeURL(url=url_list_2) + assert uut + assert uut == 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' + + +def test_metadata_object_for_url(service, url_list_1): + uut = service.MetadataObjectForURL(url=url_list_1) + assert uut + # todo - plexhints needs to be improved in order to test this properly + + +def test_media_objects_for_url(service, url_list_1): + uut = service.MediaObjectsForURL(url=url_list_1) + assert uut + assert isinstance(uut, list) + assert len(uut) > 0 + + for media in uut: + assert media + # todo - plexhints needs to be improved in order to test this properly