Ivars 3b2deaac9b 0.1.1 pilna versija | 7 lat temu | |
---|---|---|
.. | ||
mock | 7 lat temu | |
.gitignore | 7 lat temu | |
.gitmodules | 7 lat temu | |
LICENSE.md | 7 lat temu | |
README.md | 7 lat temu | |
__init__.py | 7 lat temu | |
bootstrap.py | 7 lat temu | |
bootstrap0.py | 7 lat temu | |
config.py | 7 lat temu | |
mock.py | 7 lat temu | |
plex.py | 7 lat temu |
This package is intended for Plex channel developers and aims for recreating the Plex Framework environment for unit testing the channel code. The framework runs channel code in a restricted sandbox and subjects it to various restrictions making it hard to test the code independently. This package provides means to test the channel code within the original sandbox as if it was run by the Plex Media Server.
The suggested place for the tests is a Tests
subdirectory of the Contents
directory, but it’s not required.
The most simple way to add this module to your channel is to install it as a git submodule:
Contents/Tests
directory of your channel.Add a submodule to your repo:
git submodule add https://bitbucket.org/czukowski/plex-test-case.git plex_test_case
Initialize the submodule to have it download necessary code:
git submodule update --init --recursive
We’ll use Python bundled with Plex Media Server to run the tests. To have it set up correctly, quite a lot of paths
need to be set up in PYTHONPATH
. It helps to use an IDE that can help configure it more easily and also provide a
test runner. The following setup is described for the PyCharm IDE (though know that PyCharm introduces a
couple of its own obstacles to running tests).
On Windows, the Python interpreter is a file named PlexScriptHost.exe
located in the Plex folder in Program Files.
For some reason PyCharm doesn’t want to take a file with such name as a Python interpreter, so making its copy named
python.exe
may be necessary. It might be fixed somewhere in the future.
For Plex on other operating systems things may be different.
The interpreter is set in Settings / Project Interpreter dialog.
Unit test runner that comes with PyCharm is not very well compatible with the Python version Plex Media Server
comes with. In PyCharm version 2017, a test runner tries to use a discover
module which can not be found in
Plex distribution (see PY-24057). The workaround for now is, sadly, editing a test runner helper file named
_jb_unittest_runner.py
, where you can add import discover
module and skip the
highlighted lines on ImportError
. In PyCharm versions before 2017, the test runner, utrunner.py
does not load modules from a ZIP file. To fix it, pycharm_run_utils.py
needs to be patched as described in PY-12072. This is not good as the IDE updates will overwrite those files
and you’ll need to do it again.
Python libraries and extensions must be included in PYTHONPATH
. The easy way to do it is to add them as content
roots in Settings / Project Structure dialog. The following files and directories are
needed (example again from Windows, other OS users should figure that on their own, sorry):
%ProgramFiles(x86)%\Plex\Plex Media Server\DLLs
%ProgramFiles(x86)%\Plex\Plex Media Server\Exts
%ProgramFiles(x86)%\Plex\Plex Media Server\python27.zip
Plex Framework specific code also need to be included in PYTHONPATH
. This is done the same way as above, but first
the actual base path has to be determined. It is located in a subfolder named Plug-ins-???????
under the path
%ProgramFiles(x86)%\Plex\Plex Media Server\Resources\
(question marks stand for a string that resembles a git
commit id) and may be different depending on the Plex Media Server version. Also it may change on PMS update and
the old folder deleted. A good idea may be to create a symbolic link named just Plug-ins
and re-link it with each
server update instead of re-configuring a number of paths in all projects. The following directories need to be added
as content roots:
%ProgramFiles(x86)%\Plex\Plex Media Server\Resources\Plug-ins-???????\Framework.bundle\Contents\Resources\Platforms\Shared\Libraries
%ProgramFiles(x86)%\Plex\Plex Media Server\Resources\Plug-ins-???????\Framework.bundle\Contents\Resources\Versions\2\Python
%ProgramFiles(x86)%\Plex\Plex Media Server\Resources\Plug-ins-???????\Framework.bundle\Contents\Resources\Versions\1\Python
%ProgramFiles(x86)%\Plex\Plex Media Server\Resources\Plug-ins-???????\Framework.bundle\Contents\Resources\Versions\0\Python
Note the different framework versions in the list above, their load order is actually important, but PyCharm doesn’t
seem to offer a way to add the Content Roots in a specific order. A workaround for that is implemented in bootstrap.py
to change the load order if needed.
A run configuration for unit tests needs to be added for the project. Make sure ‘Add content roots
to PYTHONPATH’ is checked. There is also an option to set environment variables that could be used to add the folders
above to PYTHONPATH
, but adding them as content roots has a benefit of offering some insight into the actual
Framework code during debugging as well as a (very) limited code completion.
It should be possible to run the tests without any IDE and its test runners. The only thing necessary would be to configure
PYTHONPATH
to include all the paths mentioned above, perhaps by creating a batch file.
Test case classes must extend the base test class, PlexTestCase
. It provides the functionality to initialize the framework
and import the tested module in its sandbox. See __init__.py
file for the implementation details. Many of it may be still
work in progress though.
To test the module, create a test case module with the same name and a _test
suffix, then the tested module may be
accessed using self.module
property. If you choose another naming convention, you’ll need to set module_name
attribute
in the test case class beforehand. Note that the tested module must be imported from Code/__init__.py
either directly
or indirectly in order for it to be added to the sandboxed environment.
It is possible to test request handling by the channel controllers, using self.request()
method.
HTTP requests made during testing will not send for real. The intention is to be able to mock these requests and return
predefined responses. The desired response body and headers may be preset using self.networking.http_response_body
and
self.networking.http_response_headers
in the test case class respectively.
There is a shortcut method to load file contents, accessible via self.get_file_contents()
. It takes a file name that
is placed under the current module’s subdirectory named after the current test case class.
Example test case module:
from plex_test_case import PlexTestCase
from Framework.api.objectkit import ObjectContainer
class MyChannelTest(PlexTestCase):
def test_main_menu(self):
# Load './MyChannelTest/MainMenuObjectContainer.xml' as expected XML generated by channel.
expected = self.get_file_contents('MainMenuObjectContainer.xml')
# Fix HTTP response from the './MyChannelTest/Index.html' file contents.
self.networking.http_response_body = self.get_file_contents('Index.html')
# Make a request to channel code (which makes a HTTP request mocked above).
status, headers, body = self.request('/video/mychannel')
# Check the return values against expected.
self.assertEquals(200, status)
self.assertEquals('application/xml', headers['Content-Type'])
self.assertEquals(expected, body)
class ParserTest(PlexTestCase):
module_name = 'parser'
def test_parse(self):
# Load './ParserTest/Homepage.html' as input for parser.
contents = self.get_file_contents('Homepage.html')
# This is what the channel code would have called as 'HTML.ElementFromString(contents)'.
# Shared Code modules (*.pys) run in a separate sandbox and could be accessed as `self.shared_code_environment`.
nav = self.environment['HTML'].ElementFromString(contents)
# Invoke `parse_categories()` function in 'parser.py' module.
actual = self.module.parse_categories(nav)
# Check actual return value against expected data structure, etc...
...
This code is distributed under the MIT License.