Merge pull request #1343 from NotAFile/add-tests

Add example unit test and config for testing
This commit is contained in:
Lonami 2019-12-14 13:19:49 +01:00 committed by GitHub
commit ccbc1c669c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 249 additions and 2 deletions

8
.coveragerc Normal file
View File

@ -0,0 +1,8 @@
[run]
branch = true
parallel = true
source =
telethon
[report]
precision = 2

28
.github/workflows/python.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: Python Library
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.5", "3.6", "3.7", "3.8"]
steps:
- uses: actions/checkout@v1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Set up env
run: |
python -m pip install --upgrade pip
pip install tox
- name: Lint with flake8
run: |
tox -e flake
- name: Test with pytest
run: |
# use "py", which is the default python version
tox -e py

3
dev-requirements.txt Normal file
View File

@ -0,0 +1,3 @@
pytest
pytest-cov
pytest-asyncio

View File

@ -1,4 +1,4 @@
cryptg
pysocks
hachoir3
hachoir
pillow

View File

@ -0,0 +1,87 @@
=====
Tests
=====
Telethon uses `Pytest <https://pytest.org/>`__, for testing, `Tox
<https://tox.readthedocs.io/en/latest/>`__ for environment setup, and
`pytest-asyncio <https://pypi.org/project/pytest-asyncio/>`__ and `pytest-cov
<https://pytest-cov.readthedocs.io/en/latest/>`__ for asyncio and
`coverage <https://coverage.readthedocs.io/>`__ integration.
While reading the full documentation for these is probably a good idea, there
is a lot to read, so a brief summary of these tools is provided below for
convienience.
Brief Introduction to Pytest
============================
`Pytest <https://pytest.org/>`__ is a tool for discovering and running python
tests, as well as allowing modular reuse of test setup code using fixtures.
Most Pytest tests will look something like this::
from module import my_thing, my_other_thing
def test_my_thing(fixture):
assert my_thing(fixture) == 42
@pytest.mark.asyncio
async def test_my_thing(event_loop):
assert await my_other_thing(loop=event_loop) == 42
Note here:
1. The test imports one specific function. The role of unit tests is to test
that the implementation of some unit, like a function or class, works.
It's role is not so much to test that components interact well with each
other. I/O, such as connecting to remote servers, should be avoided. This
helps with quickly identifying the source of an error, finding silent
breakage, and makes it easier to cover all possible code paths.
System or integration tests can also be useful, but are currently out of
scope of Telethon's automated testing.
2. A function ``test_my_thing`` is declared. Pytest searches for files
starting with ``test_``, classes starting with ``Test`` and executes any
functions or methods starting with ``test_`` it finds.
3. The function is declared with a parameter ``fixture``. Fixtures are used to
request things required to run the test, such as temporary directories,
free TCP ports, Connections, etc. Fixtures are declared by simply adding
the fixture name as parameter. A full list of available fixtures can be
found with the ``pytest --fixtures`` command.
4. The test uses a simple ``assert`` to test some condition is valid. Pytest
uses some magic to ensure that the errors from this are readable and easy
to debug.
5. The ``pytest.mark.asyncio`` fixture is provided by ``pytest-asyncio``. It
starts a loop and executes a test function as coroutine. This should be
used for testing asyncio code. It also declares the ``event_loop``
fixture, which will request an ``asyncio`` event loop.
Brief Introduction to Tox
=========================
`Tox <https://tox.readthedocs.io/en/latest/>`__ is a tool for automated setup
of virtual environments for testing. While the tests can be run directly by
just running ``pytest``, this only tests one specific python version in your
existing environment, which will not catch e.g. undeclared dependencies, or
version incompatabilities.
Tox environments are declared in the ``tox.ini`` file. The default
environments, declared at the top, can be simply run with ``tox``. The option
``tox -e py36,flake`` can be used to request specific environments to be run.
Brief Introduction to Pytest-cov
================================
Coverage is a useful metric for testing. It measures the lines of code and
branches that are exercised by the tests. The higher the coverage, the more
likely it is that any coding errors will be caught by the tests.
A brief coverage report can be generated with the ``--cov`` option to ``tox``,
which will be passed on to ``pytest``. Additionally, the very useful HTML
report can be generated with ``--cov --cov-report=html``, which contains a
browsable copy of the source code, annotated with coverage information for each
line.

View File

@ -93,6 +93,7 @@ You can also use the menu on the left to quickly skip over sections.
developing/test-servers.rst
developing/project-structure.rst
developing/coding-style.rst
developing/testing.rst
developing/understanding-the-type-language.rst
developing/tips-for-porting-the-project.rst
developing/telegram-api-in-other-languages.rst

View File

@ -662,7 +662,7 @@ class TelegramBaseClient(abc.ABC):
self._exported_sessions[cdn_redirect.dc_id] = session
self._log[__name__].info('Creating new CDN client')
client = TelegramBareClient(
client = TelegramBaseClient(
session, self.api_id, self.api_hash,
proxy=self._sender.connection.conn.proxy,
timeout=self._sender.connection.get_timeout()

0
tests/__init__.py Normal file
View File

View File

View File

@ -0,0 +1,37 @@
"""
tests for telethon.crypto.rsa
"""
import pytest
from telethon.crypto import rsa
@pytest.fixture
def server_key_fp():
"""factory to return a key, old if so chosen"""
def _server_key_fp(old: bool):
for fp, data in rsa._server_keys.items():
_, old_key = data
if old_key == old:
return fp
return _server_key_fp
def test_encryption_inv_key():
"""test for #1324"""
assert rsa.encrypt("invalid", b"testdata") is None
def test_encryption_old_key(server_key_fp):
"""test for #1324"""
assert rsa.encrypt(server_key_fp(old=True), b"testdata") is None
def test_encryption_allowed_old_key(server_key_fp):
data = rsa.encrypt(server_key_fp(old=True), b"testdata", use_old=True)
# we can't verify the data is actually valid because we don't have
# the decryption keys
assert data is not None and len(data) == 256
def test_encryption_current_key(server_key_fp):
data = rsa.encrypt(server_key_fp(old=False), b"testdata")
# we can't verify the data is actually valid because we don't have
# the decryption keys
assert data is not None and len(data) == 256

View File

@ -0,0 +1,59 @@
"""
tests for telethon.helpers
"""
from base64 import b64decode
import pytest
from telethon import helpers
def test_strip_text():
assert helpers.strip_text(" text ", []) == "text"
# I can't interpret the rest of the code well enough yet
class TestSyncifyAsyncContext:
class NoopContextManager:
def __init__(self, loop):
self.count = 0
self.loop = loop
async def __aenter__(self):
self.count += 1
return self
async def __aexit__(self, exc_type, *args):
assert exc_type is None
self.count -= 1
__enter__ = helpers._sync_enter
__exit__ = helpers._sync_exit
def test_sync_acontext(self, event_loop):
contm = self.NoopContextManager(event_loop)
assert contm.count == 0
with contm:
assert contm.count == 1
assert contm.count == 0
@pytest.mark.asyncio
async def test_async_acontext(self, event_loop):
contm = self.NoopContextManager(event_loop)
assert contm.count == 0
async with contm:
assert contm.count == 1
assert contm.count == 0
def test_generate_key_data_from_nonce():
gkdfn = helpers.generate_key_data_from_nonce
key_expect = b64decode(b'NFwRFB8Knw/kAmvPWjtrQauWysHClVfQh0UOAaABqZA=')
nonce_expect = b64decode(b'1AgjhU9eDvJRjFik73bjR2zZEATzL/jLu9yodYfWEgA=')
assert gkdfn(123456789, 1234567) == (key_expect, nonce_expect)

24
tox.ini Normal file
View File

@ -0,0 +1,24 @@
[tox]
envlist = py35,py36,py37,py38
[testenv]
deps =
-rrequirements.txt
-roptional-requirements.txt
-rdev-requirements.txt
commands =
# NOTE: you can run any command line tool here - not just tests
pytest {posargs}
# run with tox -e flake
[testenv:flake]
deps =
-rrequirements.txt
-roptional-requirements.txt
-rdev-requirements.txt
flake8
commands =
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --exclude telethon/tl/,telethon/errors/rpcerrorlist.py --max-complexity=10 --max-line-length=127 --statistics