mirror of
https://github.com/onionshare/onionshare.git
synced 2025-01-03 11:40:58 -05:00
Merge branch 'delirious-lettuce-test_onionshare_common'
This commit is contained in:
commit
2a8a7fd634
10
.travis.yml
10
.travis.yml
@ -1,5 +1,5 @@
|
|||||||
language: python
|
language: python
|
||||||
sudo: required
|
# sudo: required
|
||||||
dist: trusty
|
dist: trusty
|
||||||
python:
|
python:
|
||||||
- "3.4"
|
- "3.4"
|
||||||
@ -9,7 +9,9 @@ python:
|
|||||||
- "3.7-dev"
|
- "3.7-dev"
|
||||||
- "nightly"
|
- "nightly"
|
||||||
# command to install dependencies
|
# command to install dependencies
|
||||||
before_install: "sudo apt-get update; sudo apt-get install -y python3-nose python3-flask python3-stem python3-pyqt5"
|
install:
|
||||||
install: ""
|
- pip install Flask==0.12 stem==1.5.4 pytest-cov coveralls
|
||||||
# command to run tests
|
# command to run tests
|
||||||
script: nosetests3
|
script: pytest --cov=onionshare test/
|
||||||
|
after_success:
|
||||||
|
- coveralls
|
||||||
|
10
BUILD.md
10
BUILD.md
@ -11,9 +11,9 @@ cd onionshare
|
|||||||
|
|
||||||
Install the needed dependencies:
|
Install the needed dependencies:
|
||||||
|
|
||||||
For Debian-like distros: `apt install -y build-essential fakeroot python3-all python3-stdeb dh-python python3-flask python3-stem python3-pyqt5 python-nautilus python3-nose tor`
|
For Debian-like distros: `apt install -y build-essential fakeroot python3-all python3-stdeb dh-python python3-flask python3-stem python3-pyqt5 python-nautilus python3-pytest tor`
|
||||||
|
|
||||||
For Fedora-like distros: `dnf install -y rpm-build python3-flask python3-stem python3-qt5 nautilus-python tor`
|
For Fedora-like distros: `dnf install -y rpm-build python3-flask python3-stem python3-qt5 python3-pytest nautilus-python tor`
|
||||||
|
|
||||||
After that you can try both the CLI and the GUI version of OnionShare:
|
After that you can try both the CLI and the GUI version of OnionShare:
|
||||||
|
|
||||||
@ -126,10 +126,8 @@ This will prompt you to codesign three binaries and execute one unsigned binary.
|
|||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
OnionShare includes [nose](https://nose.readthedocs.org/en/latest/) unit tests. First, `sudo apt-get install python3-nose` or `sudo pip3 install nose`.
|
OnionShare includes PyTest unit tests. To run the tests:
|
||||||
|
|
||||||
To run the tests:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
nosetests3 test
|
pytest test/
|
||||||
```
|
```
|
||||||
|
@ -2,5 +2,3 @@
|
|||||||
|
|
||||||
# Pre-push hook. If you want to test with a different version of firefox, put
|
# Pre-push hook. If you want to test with a different version of firefox, put
|
||||||
# the path in the CFX_FIREFOX environment variable.
|
# the path in the CFX_FIREFOX environment variable.
|
||||||
|
|
||||||
nosetests test
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
Package3: onionshare
|
Package3: onionshare
|
||||||
Depends3: python3-flask, python3-stem, python3-pyqt5, python-nautilus, tor
|
Depends3: python3-flask, python3-stem, python3-pyqt5, python-nautilus, tor
|
||||||
Build-Depends: python3-nose, python3-flask, python3-stem, python3-pyqt5
|
Build-Depends: python3-pytest, python3-flask, python3-stem, python3-pyqt5
|
||||||
Suite: xenial
|
Suite: xenial
|
||||||
X-Python3-Version: >= 3.4
|
X-Python3-Version: >= 3.4
|
||||||
|
0
test/__init__.py
Normal file
0
test/__init__.py
Normal file
157
test/conftest.py
Normal file
157
test/conftest.py
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from onionshare import common
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def temp_dir_1024():
|
||||||
|
""" Create a temporary directory that has a single file of a
|
||||||
|
particular size (1024 bytes).
|
||||||
|
"""
|
||||||
|
|
||||||
|
tmp_dir = tempfile.mkdtemp()
|
||||||
|
tmp_file, tmp_file_path = tempfile.mkstemp(dir=tmp_dir)
|
||||||
|
with open(tmp_file, 'wb') as f:
|
||||||
|
f.write(b'*' * 1024)
|
||||||
|
return tmp_dir
|
||||||
|
|
||||||
|
|
||||||
|
# pytest > 2.9 only needs @pytest.fixture
|
||||||
|
@pytest.yield_fixture
|
||||||
|
def temp_dir_1024_delete():
|
||||||
|
""" Create a temporary directory that has a single file of a
|
||||||
|
particular size (1024 bytes). The temporary directory (including
|
||||||
|
the file inside) will be deleted after fixture usage.
|
||||||
|
"""
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||||
|
tmp_file, tmp_file_path = tempfile.mkstemp(dir=tmp_dir)
|
||||||
|
with open(tmp_file, 'wb') as f:
|
||||||
|
f.write(b'*' * 1024)
|
||||||
|
yield tmp_dir
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def temp_file_1024():
|
||||||
|
""" Create a temporary file of a particular size (1024 bytes). """
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
|
||||||
|
tmp_file.write(b'*' * 1024)
|
||||||
|
return tmp_file.name
|
||||||
|
|
||||||
|
|
||||||
|
# pytest > 2.9 only needs @pytest.fixture
|
||||||
|
@pytest.yield_fixture
|
||||||
|
def temp_file_1024_delete():
|
||||||
|
"""
|
||||||
|
Create a temporary file of a particular size (1024 bytes).
|
||||||
|
The temporary file will be deleted after fixture usage.
|
||||||
|
"""
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile() as tmp_file:
|
||||||
|
tmp_file.write(b'*' * 1024)
|
||||||
|
tmp_file.flush()
|
||||||
|
yield tmp_file.name
|
||||||
|
|
||||||
|
|
||||||
|
# pytest > 2.9 only needs @pytest.fixture
|
||||||
|
@pytest.yield_fixture(scope='session')
|
||||||
|
def custom_zw():
|
||||||
|
zw = common.ZipWriter(
|
||||||
|
zip_filename=common.random_string(4, 6),
|
||||||
|
processed_size_callback=lambda _: 'custom_callback'
|
||||||
|
)
|
||||||
|
yield zw
|
||||||
|
zw.close()
|
||||||
|
os.remove(zw.zip_filename)
|
||||||
|
|
||||||
|
|
||||||
|
# pytest > 2.9 only needs @pytest.fixture
|
||||||
|
@pytest.yield_fixture(scope='session')
|
||||||
|
def default_zw():
|
||||||
|
zw = common.ZipWriter()
|
||||||
|
yield zw
|
||||||
|
zw.close()
|
||||||
|
tmp_dir = os.path.dirname(zw.zip_filename)
|
||||||
|
shutil.rmtree(tmp_dir)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def locale_en(monkeypatch):
|
||||||
|
monkeypatch.setattr('locale.getdefaultlocale', lambda: ('en_US', 'UTF-8'))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def locale_fr(monkeypatch):
|
||||||
|
monkeypatch.setattr('locale.getdefaultlocale', lambda: ('fr_FR', 'UTF-8'))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def locale_invalid(monkeypatch):
|
||||||
|
monkeypatch.setattr('locale.getdefaultlocale', lambda: ('xx_XX', 'UTF-8'))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def locale_ru(monkeypatch):
|
||||||
|
monkeypatch.setattr('locale.getdefaultlocale', lambda: ('ru_RU', 'UTF-8'))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def platform_darwin(monkeypatch):
|
||||||
|
monkeypatch.setattr('platform.system', lambda: 'Darwin')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture # (scope="session")
|
||||||
|
def platform_linux(monkeypatch):
|
||||||
|
monkeypatch.setattr('platform.system', lambda: 'Linux')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def platform_windows(monkeypatch):
|
||||||
|
monkeypatch.setattr('platform.system', lambda: 'Windows')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def set_debug_false(monkeypatch):
|
||||||
|
monkeypatch.setattr('onionshare.common.debug', False)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def set_debug_true(monkeypatch):
|
||||||
|
monkeypatch.setattr('onionshare.common.debug', True)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sys_argv_sys_prefix(monkeypatch):
|
||||||
|
monkeypatch.setattr('sys.argv', [sys.prefix])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sys_frozen(monkeypatch):
|
||||||
|
monkeypatch.setattr('sys.frozen', True, raising=False)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sys_meipass(monkeypatch):
|
||||||
|
monkeypatch.setattr(
|
||||||
|
'sys._MEIPASS', os.path.expanduser('~'), raising=False)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture # (scope="session")
|
||||||
|
def sys_onionshare_dev_mode(monkeypatch):
|
||||||
|
monkeypatch.setattr('sys.onionshare_dev_mode', True, raising=False)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def time_time_100(monkeypatch):
|
||||||
|
monkeypatch.setattr('time.time', lambda: 100)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def time_strftime(monkeypatch):
|
||||||
|
monkeypatch.setattr('time.strftime', lambda _: 'Jun 06 2013 11:05:00')
|
@ -16,20 +16,341 @@ GNU General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import inspect
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import re
|
||||||
import socket
|
import socket
|
||||||
|
import sys
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from onionshare import common
|
from onionshare import common
|
||||||
|
|
||||||
|
DEFAULT_ZW_FILENAME_REGEX = re.compile(r'^onionshare_[a-z2-7]{6}.zip$')
|
||||||
|
LOG_MSG_REGEX = re.compile(r"""
|
||||||
|
^\[Jun\ 06\ 2013\ 11:05:00\]
|
||||||
|
\ TestModule\.<function\ TestLog\.test_output\.<locals>\.dummy_func
|
||||||
|
\ at\ 0x[a-f0-9]+>(:\ TEST_MSG)?$""", re.VERBOSE)
|
||||||
|
RANDOM_STR_REGEX = re.compile(r'^[a-z2-7]+$')
|
||||||
|
SLUG_REGEX = re.compile(r'^([a-z]+)(-[a-z]+)?-([a-z]+)(-[a-z]+)?$')
|
||||||
|
|
||||||
def test_get_platform_returns_platform_system():
|
|
||||||
"""get_platform() returns platform.system() when ONIONSHARE_PLATFORM is not defined"""
|
|
||||||
p = common.platform.system
|
|
||||||
common.platform.system = lambda: 'Sega Saturn'
|
|
||||||
assert common.get_platform() == 'Sega Saturn'
|
|
||||||
common.platform.system = p
|
|
||||||
|
|
||||||
def test_get_available_port_returns_an_open_port():
|
class TestBuildSlug:
|
||||||
"""get_available_port() should return an open port within the range"""
|
@pytest.mark.parametrize('test_input,expected', (
|
||||||
for i in range(100):
|
# VALID, two lowercase words, separated by a hyphen
|
||||||
port = common.get_available_port(1024, 2048)
|
('syrup-enzyme', True),
|
||||||
assert 1024 <= port <= 2048
|
('caution-friday', True),
|
||||||
socket.socket().bind(("127.0.0.1", port))
|
|
||||||
|
# VALID, two lowercase words, with one hyphenated compound word
|
||||||
|
('drop-down-thimble', True),
|
||||||
|
('unmixed-yo-yo', True),
|
||||||
|
|
||||||
|
# VALID, two lowercase hyphenated compound words, separated by hyphen
|
||||||
|
('yo-yo-drop-down', True),
|
||||||
|
('felt-tip-t-shirt', True),
|
||||||
|
('hello-world', True),
|
||||||
|
|
||||||
|
# INVALID
|
||||||
|
('Upper-Case', False),
|
||||||
|
('digits-123', False),
|
||||||
|
('too-many-hyphens-', False),
|
||||||
|
('symbols-!@#$%', False)
|
||||||
|
))
|
||||||
|
def test_build_slug_regex(self, test_input, expected):
|
||||||
|
""" Test that `SLUG_REGEX` accounts for the following patterns
|
||||||
|
|
||||||
|
There are a few hyphenated words in `wordlist.txt`:
|
||||||
|
* drop-down
|
||||||
|
* felt-tip
|
||||||
|
* t-shirt
|
||||||
|
* yo-yo
|
||||||
|
|
||||||
|
These words cause a few extra potential slug patterns:
|
||||||
|
* word-word
|
||||||
|
* hyphenated-word-word
|
||||||
|
* word-hyphenated-word
|
||||||
|
* hyphenated-word-hyphenated-word
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert bool(SLUG_REGEX.match(test_input)) == expected
|
||||||
|
|
||||||
|
def test_build_slug_unique(self, sys_onionshare_dev_mode):
|
||||||
|
assert common.build_slug() != common.build_slug()
|
||||||
|
|
||||||
|
|
||||||
|
class TestDirSize:
|
||||||
|
def test_temp_dir_size(self, temp_dir_1024_delete):
|
||||||
|
""" dir_size() should return the total size (in bytes) of all files
|
||||||
|
in a particular directory.
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert common.dir_size(temp_dir_1024_delete) == 1024
|
||||||
|
|
||||||
|
|
||||||
|
class TestEstimatedTimeRemaining:
|
||||||
|
@pytest.mark.parametrize('test_input,expected', (
|
||||||
|
((2, 676, 12), '8h14m16s'),
|
||||||
|
((14, 1049, 30), '1h26m15s'),
|
||||||
|
((21, 450, 1), '33m42s'),
|
||||||
|
((31, 1115, 80), '11m39s'),
|
||||||
|
((336, 989, 32), '2m12s'),
|
||||||
|
((603, 949, 38), '36s'),
|
||||||
|
((971, 1009, 83), '1s')
|
||||||
|
))
|
||||||
|
def test_estimated_time_remaining(
|
||||||
|
self, test_input, expected, time_time_100):
|
||||||
|
assert common.estimated_time_remaining(*test_input) == expected
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('test_input', (
|
||||||
|
(10, 20, 100), # if `time_elapsed == 0`
|
||||||
|
(0, 37, 99) # if `download_rate == 0`
|
||||||
|
))
|
||||||
|
def test_raises_zero_division_error(self, test_input, time_time_100):
|
||||||
|
with pytest.raises(ZeroDivisionError):
|
||||||
|
common.estimated_time_remaining(*test_input)
|
||||||
|
|
||||||
|
|
||||||
|
class TestFormatSeconds:
|
||||||
|
@pytest.mark.parametrize('test_input,expected', (
|
||||||
|
(0, '0s'),
|
||||||
|
(26, '26s'),
|
||||||
|
(60, '1m'),
|
||||||
|
(947.35, '15m47s'),
|
||||||
|
(1847, '30m47s'),
|
||||||
|
(2193.94, '36m34s'),
|
||||||
|
(3600, '1h'),
|
||||||
|
(13426.83, '3h43m47s'),
|
||||||
|
(16293, '4h31m33s'),
|
||||||
|
(18392.14, '5h6m32s'),
|
||||||
|
(86400, '1d'),
|
||||||
|
(129674, '1d12h1m14s'),
|
||||||
|
(56404.12, '15h40m4s')
|
||||||
|
))
|
||||||
|
def test_format_seconds(self, test_input, expected):
|
||||||
|
assert common.format_seconds(test_input) == expected
|
||||||
|
|
||||||
|
# TODO: test negative numbers?
|
||||||
|
@pytest.mark.parametrize('test_input', (
|
||||||
|
'string', lambda: None, [], {}, set()
|
||||||
|
))
|
||||||
|
def test_invalid_input_types(self, test_input):
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
common.format_seconds(test_input)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetAvailablePort:
|
||||||
|
@pytest.mark.parametrize('port_min,port_max', (
|
||||||
|
(random.randint(1024, 1500),
|
||||||
|
random.randint(1800, 2048)) for _ in range(50)
|
||||||
|
))
|
||||||
|
def test_returns_an_open_port(self, port_min, port_max):
|
||||||
|
""" get_available_port() should return an open port within the range """
|
||||||
|
|
||||||
|
port = common.get_available_port(port_min, port_max)
|
||||||
|
assert port_min <= port <= port_max
|
||||||
|
with socket.socket() as tmpsock:
|
||||||
|
tmpsock.bind(('127.0.0.1', port))
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetPlatform:
|
||||||
|
def test_darwin(self, platform_darwin):
|
||||||
|
assert common.get_platform() == 'Darwin'
|
||||||
|
|
||||||
|
def test_linux(self, platform_linux):
|
||||||
|
assert common.get_platform() == 'Linux'
|
||||||
|
|
||||||
|
def test_windows(self, platform_windows):
|
||||||
|
assert common.get_platform() == 'Windows'
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: double-check these tests
|
||||||
|
class TestGetResourcePath:
|
||||||
|
def test_onionshare_dev_mode(self, sys_onionshare_dev_mode):
|
||||||
|
prefix = os.path.join(
|
||||||
|
os.path.dirname(
|
||||||
|
os.path.dirname(
|
||||||
|
os.path.abspath(
|
||||||
|
inspect.getfile(
|
||||||
|
inspect.currentframe())))), 'share')
|
||||||
|
assert (
|
||||||
|
common.get_resource_path(os.path.join(prefix, 'test_filename')) ==
|
||||||
|
os.path.join(prefix, 'test_filename'))
|
||||||
|
|
||||||
|
def test_linux(self, platform_linux, sys_argv_sys_prefix):
|
||||||
|
prefix = os.path.join(sys.prefix, 'share/onionshare')
|
||||||
|
assert (
|
||||||
|
common.get_resource_path(os.path.join(prefix, 'test_filename')) ==
|
||||||
|
os.path.join(prefix, 'test_filename'))
|
||||||
|
|
||||||
|
def test_frozen_darwin(self, platform_darwin, sys_frozen, sys_meipass):
|
||||||
|
prefix = os.path.join(sys._MEIPASS, 'share')
|
||||||
|
assert (
|
||||||
|
common.get_resource_path(os.path.join(prefix, 'test_filename')) ==
|
||||||
|
os.path.join(prefix, 'test_filename'))
|
||||||
|
|
||||||
|
def test_frozen_windows(self, platform_windows, sys_frozen):
|
||||||
|
prefix = os.path.join(os.path.dirname(sys.executable), 'share')
|
||||||
|
assert (
|
||||||
|
common.get_resource_path(os.path.join(prefix, 'test_filename')) ==
|
||||||
|
os.path.join(prefix, 'test_filename'))
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetTorPaths:
|
||||||
|
# @pytest.mark.skipif(sys.platform != 'Darwin', reason='requires MacOS') ?
|
||||||
|
def test_get_tor_paths_darwin(self, platform_darwin, sys_frozen, sys_meipass):
|
||||||
|
base_path = os.path.dirname(
|
||||||
|
os.path.dirname(
|
||||||
|
os.path.dirname(
|
||||||
|
common.get_resource_path(''))))
|
||||||
|
tor_path = os.path.join(
|
||||||
|
base_path, 'Resources', 'Tor', 'tor')
|
||||||
|
tor_geo_ip_file_path = os.path.join(
|
||||||
|
base_path, 'Resources', 'Tor', 'geoip')
|
||||||
|
tor_geo_ipv6_file_path = os.path.join(
|
||||||
|
base_path, 'Resources', 'Tor', 'geoip6')
|
||||||
|
assert (common.get_tor_paths() ==
|
||||||
|
(tor_path, tor_geo_ip_file_path, tor_geo_ipv6_file_path))
|
||||||
|
|
||||||
|
# @pytest.mark.skipif(sys.platform != 'Linux', reason='requires Linux') ?
|
||||||
|
def test_get_tor_paths_linux(self, platform_linux):
|
||||||
|
assert (common.get_tor_paths() ==
|
||||||
|
('/usr/bin/tor', '/usr/share/tor/geoip', '/usr/share/tor/geoip6'))
|
||||||
|
|
||||||
|
# @pytest.mark.skipif(sys.platform != 'Windows', reason='requires Windows') ?
|
||||||
|
def test_get_tor_paths_windows(self, platform_windows, sys_frozen):
|
||||||
|
base_path = os.path.join(
|
||||||
|
os.path.dirname(
|
||||||
|
os.path.dirname(
|
||||||
|
common.get_resource_path(''))), 'tor')
|
||||||
|
tor_path = os.path.join(
|
||||||
|
os.path.join(base_path, 'Tor'), "tor.exe")
|
||||||
|
tor_geo_ip_file_path = os.path.join(
|
||||||
|
os.path.join(
|
||||||
|
os.path.join(base_path, 'Data'), 'Tor'), 'geoip')
|
||||||
|
tor_geo_ipv6_file_path = os.path.join(
|
||||||
|
os.path.join(
|
||||||
|
os.path.join(base_path, 'Data'), 'Tor'), 'geoip6')
|
||||||
|
assert (common.get_tor_paths() ==
|
||||||
|
(tor_path, tor_geo_ip_file_path, tor_geo_ipv6_file_path))
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetVersion:
|
||||||
|
def test_get_version(self, sys_onionshare_dev_mode):
|
||||||
|
with open(common.get_resource_path('version.txt')) as f:
|
||||||
|
version = f.read().strip()
|
||||||
|
|
||||||
|
assert version == common.get_version()
|
||||||
|
|
||||||
|
|
||||||
|
class TestHumanReadableFilesize:
|
||||||
|
@pytest.mark.parametrize('test_input,expected', (
|
||||||
|
(1024 ** 0, '1.0 B'),
|
||||||
|
(1024 ** 1, '1.0 KiB'),
|
||||||
|
(1024 ** 2, '1.0 MiB'),
|
||||||
|
(1024 ** 3, '1.0 GiB'),
|
||||||
|
(1024 ** 4, '1.0 TiB'),
|
||||||
|
(1024 ** 5, '1.0 PiB'),
|
||||||
|
(1024 ** 6, '1.0 EiB'),
|
||||||
|
(1024 ** 7, '1.0 ZiB'),
|
||||||
|
(1024 ** 8, '1.0 YiB')
|
||||||
|
))
|
||||||
|
def test_human_readable_filesize(self, test_input, expected):
|
||||||
|
assert common.human_readable_filesize(test_input) == expected
|
||||||
|
|
||||||
|
|
||||||
|
class TestLog:
|
||||||
|
@pytest.mark.parametrize('test_input', (
|
||||||
|
('[Jun 06 2013 11:05:00]'
|
||||||
|
' TestModule.<function TestLog.test_output.<locals>.dummy_func'
|
||||||
|
' at 0xdeadbeef>'),
|
||||||
|
('[Jun 06 2013 11:05:00]'
|
||||||
|
' TestModule.<function TestLog.test_output.<locals>.dummy_func'
|
||||||
|
' at 0xdeadbeef>: TEST_MSG')
|
||||||
|
))
|
||||||
|
def test_log_msg_regex(self, test_input):
|
||||||
|
assert bool(LOG_MSG_REGEX.match(test_input))
|
||||||
|
|
||||||
|
def test_output(self, set_debug_true, time_strftime):
|
||||||
|
def dummy_func():
|
||||||
|
pass
|
||||||
|
|
||||||
|
# From: https://stackoverflow.com/questions/1218933
|
||||||
|
with io.StringIO() as buf, contextlib.redirect_stdout(buf):
|
||||||
|
common.log('TestModule', dummy_func)
|
||||||
|
common.log('TestModule', dummy_func, 'TEST_MSG')
|
||||||
|
output = buf.getvalue()
|
||||||
|
|
||||||
|
line_one, line_two, _ = output.split('\n')
|
||||||
|
assert LOG_MSG_REGEX.match(line_one)
|
||||||
|
assert LOG_MSG_REGEX.match(line_two)
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetDebug:
|
||||||
|
def test_debug_true(self, set_debug_false):
|
||||||
|
common.set_debug(True)
|
||||||
|
assert common.debug is True
|
||||||
|
|
||||||
|
def test_debug_false(self, set_debug_true):
|
||||||
|
common.set_debug(False)
|
||||||
|
assert common.debug is False
|
||||||
|
|
||||||
|
|
||||||
|
class TestZipWriterDefault:
|
||||||
|
@pytest.mark.parametrize('test_input', (
|
||||||
|
'onionshare_{}.zip'.format(''.join(
|
||||||
|
random.choice('abcdefghijklmnopqrstuvwxyz234567') for _ in range(6)
|
||||||
|
)) for _ in range(50)
|
||||||
|
))
|
||||||
|
def test_default_zw_filename_regex(self, test_input):
|
||||||
|
assert bool(DEFAULT_ZW_FILENAME_REGEX.match(test_input))
|
||||||
|
|
||||||
|
def test_zw_filename(self, default_zw):
|
||||||
|
zw_filename = os.path.basename(default_zw.zip_filename)
|
||||||
|
assert bool(DEFAULT_ZW_FILENAME_REGEX.match(zw_filename))
|
||||||
|
|
||||||
|
def test_zipfile_filename_matches_zipwriter_filename(self, default_zw):
|
||||||
|
assert default_zw.z.filename == default_zw.zip_filename
|
||||||
|
|
||||||
|
def test_zipfile_allow_zip64(self, default_zw):
|
||||||
|
assert default_zw.z._allowZip64 is True
|
||||||
|
|
||||||
|
def test_zipfile_mode(self, default_zw):
|
||||||
|
assert default_zw.z.mode == 'w'
|
||||||
|
|
||||||
|
def test_callback(self, default_zw):
|
||||||
|
assert default_zw.processed_size_callback(None) is None
|
||||||
|
|
||||||
|
def test_add_file(self, default_zw, temp_file_1024_delete):
|
||||||
|
default_zw.add_file(temp_file_1024_delete)
|
||||||
|
zipfile_info = default_zw.z.getinfo(
|
||||||
|
os.path.basename(temp_file_1024_delete))
|
||||||
|
|
||||||
|
assert zipfile_info.compress_type == zipfile.ZIP_DEFLATED
|
||||||
|
assert zipfile_info.file_size == 1024
|
||||||
|
|
||||||
|
def test_add_directory(self, temp_dir_1024_delete, default_zw):
|
||||||
|
previous_size = default_zw._size # size before adding directory
|
||||||
|
default_zw.add_dir(temp_dir_1024_delete)
|
||||||
|
assert default_zw._size == previous_size + 1024
|
||||||
|
|
||||||
|
|
||||||
|
class TestZipWriterCustom:
|
||||||
|
@pytest.mark.parametrize('test_input', (
|
||||||
|
common.random_string(
|
||||||
|
random.randint(2, 50),
|
||||||
|
random.choice((None, random.randint(2, 50)))
|
||||||
|
) for _ in range(50)
|
||||||
|
))
|
||||||
|
def test_random_string_regex(self, test_input):
|
||||||
|
assert bool(RANDOM_STR_REGEX.match(test_input))
|
||||||
|
|
||||||
|
def test_custom_filename(self, custom_zw):
|
||||||
|
assert bool(RANDOM_STR_REGEX.match(custom_zw.zip_filename))
|
||||||
|
|
||||||
|
def test_custom_callback(self, custom_zw):
|
||||||
|
assert custom_zw.processed_size_callback(None) == 'custom_callback'
|
||||||
|
163
test/onionshare_settings_test.py
Normal file
163
test/onionshare_settings_test.py
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
"""
|
||||||
|
OnionShare | https://onionshare.org/
|
||||||
|
|
||||||
|
Copyright (C) 2017 Micah Lee <micah@micahflee.com>
|
||||||
|
|
||||||
|
This program 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.
|
||||||
|
|
||||||
|
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from onionshare import common, settings, strings
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def custom_version(monkeypatch):
|
||||||
|
monkeypatch.setattr(common, 'get_version', lambda: 'DUMMY_VERSION_1.2.3')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def os_path_expanduser(monkeypatch):
|
||||||
|
monkeypatch.setattr('os.path.expanduser', lambda path: path)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def settings_obj(custom_version, sys_onionshare_dev_mode, platform_linux):
|
||||||
|
return settings.Settings()
|
||||||
|
|
||||||
|
|
||||||
|
class TestSettings:
|
||||||
|
def test_init(self, settings_obj):
|
||||||
|
assert settings_obj._settings == settings_obj.default_settings == {
|
||||||
|
'version': 'DUMMY_VERSION_1.2.3',
|
||||||
|
'connection_type': 'bundled',
|
||||||
|
'control_port_address': '127.0.0.1',
|
||||||
|
'control_port_port': 9051,
|
||||||
|
'socks_address': '127.0.0.1',
|
||||||
|
'socks_port': 9050,
|
||||||
|
'socket_file_path': '/var/run/tor/control',
|
||||||
|
'auth_type': 'no_auth',
|
||||||
|
'auth_password': '',
|
||||||
|
'close_after_first_download': True,
|
||||||
|
'systray_notifications': True,
|
||||||
|
'use_stealth': False,
|
||||||
|
'use_autoupdate': True,
|
||||||
|
'autoupdate_timestamp': None
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_fill_in_defaults(self, settings_obj):
|
||||||
|
del settings_obj._settings['version']
|
||||||
|
settings_obj.fill_in_defaults()
|
||||||
|
assert settings_obj._settings['version'] == 'DUMMY_VERSION_1.2.3'
|
||||||
|
|
||||||
|
def test_load(self, settings_obj):
|
||||||
|
custom_settings = {
|
||||||
|
'version': 'CUSTOM_VERSION',
|
||||||
|
'socks_port': 9999,
|
||||||
|
'use_stealth': True
|
||||||
|
}
|
||||||
|
tmp_file, tmp_file_path = tempfile.mkstemp()
|
||||||
|
with open(tmp_file, 'w') as f:
|
||||||
|
json.dump(custom_settings, f)
|
||||||
|
settings_obj.filename = tmp_file_path
|
||||||
|
settings_obj.load()
|
||||||
|
|
||||||
|
assert settings_obj._settings['version'] == 'CUSTOM_VERSION'
|
||||||
|
assert settings_obj._settings['socks_port'] == 9999
|
||||||
|
assert settings_obj._settings['use_stealth'] is True
|
||||||
|
|
||||||
|
os.remove(tmp_file_path)
|
||||||
|
assert os.path.exists(tmp_file_path) is False
|
||||||
|
|
||||||
|
def test_save(self, monkeypatch, settings_obj):
|
||||||
|
monkeypatch.setattr(strings, '_', lambda _: '')
|
||||||
|
|
||||||
|
settings_filename = 'default_settings.json'
|
||||||
|
tmp_dir = tempfile.gettempdir()
|
||||||
|
settings_path = os.path.join(tmp_dir, settings_filename)
|
||||||
|
settings_obj.filename = settings_path
|
||||||
|
settings_obj.save()
|
||||||
|
with open(settings_path, 'r') as f:
|
||||||
|
settings = json.load(f)
|
||||||
|
|
||||||
|
assert settings_obj._settings == settings
|
||||||
|
|
||||||
|
os.remove(settings_path)
|
||||||
|
assert os.path.exists(settings_path) is False
|
||||||
|
|
||||||
|
def test_get(self, settings_obj):
|
||||||
|
assert settings_obj.get('version') == 'DUMMY_VERSION_1.2.3'
|
||||||
|
assert settings_obj.get('connection_type') == 'bundled'
|
||||||
|
assert settings_obj.get('control_port_address') == '127.0.0.1'
|
||||||
|
assert settings_obj.get('control_port_port') == 9051
|
||||||
|
assert settings_obj.get('socks_address') == '127.0.0.1'
|
||||||
|
assert settings_obj.get('socks_port') == 9050
|
||||||
|
assert settings_obj.get('socket_file_path') == '/var/run/tor/control'
|
||||||
|
assert settings_obj.get('auth_type') == 'no_auth'
|
||||||
|
assert settings_obj.get('auth_password') == ''
|
||||||
|
assert settings_obj.get('close_after_first_download') is True
|
||||||
|
assert settings_obj.get('systray_notifications') is True
|
||||||
|
assert settings_obj.get('use_stealth') is False
|
||||||
|
assert settings_obj.get('use_autoupdate') is True
|
||||||
|
assert settings_obj.get('autoupdate_timestamp') is None
|
||||||
|
|
||||||
|
def test_set_version(self, settings_obj):
|
||||||
|
settings_obj.set('version', 'CUSTOM_VERSION')
|
||||||
|
assert settings_obj._settings['version'] == 'CUSTOM_VERSION'
|
||||||
|
|
||||||
|
def test_set_control_port_port(self, settings_obj):
|
||||||
|
settings_obj.set('control_port_port', 999)
|
||||||
|
assert settings_obj._settings['control_port_port'] == 999
|
||||||
|
|
||||||
|
settings_obj.set('control_port_port', 'NON_INTEGER')
|
||||||
|
assert settings_obj._settings['control_port_port'] == 9051
|
||||||
|
|
||||||
|
def test_set_socks_port(self, settings_obj):
|
||||||
|
settings_obj.set('socks_port', 888)
|
||||||
|
assert settings_obj._settings['socks_port'] == 888
|
||||||
|
|
||||||
|
settings_obj.set('socks_port', 'NON_INTEGER')
|
||||||
|
assert settings_obj._settings['socks_port'] == 9050
|
||||||
|
|
||||||
|
def test_filename_darwin(
|
||||||
|
self,
|
||||||
|
custom_version,
|
||||||
|
monkeypatch,
|
||||||
|
os_path_expanduser,
|
||||||
|
platform_darwin):
|
||||||
|
obj = settings.Settings()
|
||||||
|
assert (obj.filename ==
|
||||||
|
'~/Library/Application Support/OnionShare/onionshare.json')
|
||||||
|
|
||||||
|
def test_filename_linux(
|
||||||
|
self,
|
||||||
|
custom_version,
|
||||||
|
monkeypatch,
|
||||||
|
os_path_expanduser,
|
||||||
|
platform_linux):
|
||||||
|
obj = settings.Settings()
|
||||||
|
assert obj.filename == '~/.config/onionshare/onionshare.json'
|
||||||
|
|
||||||
|
def test_filename_windows(
|
||||||
|
self,
|
||||||
|
custom_version,
|
||||||
|
monkeypatch,
|
||||||
|
platform_windows):
|
||||||
|
monkeypatch.setenv('APPDATA', 'C:')
|
||||||
|
obj = settings.Settings()
|
||||||
|
assert obj.filename == 'C:\\OnionShare\\onionshare.json'
|
@ -17,30 +17,55 @@ GNU General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
import locale, os
|
|
||||||
|
import types
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from onionshare import common, strings
|
from onionshare import common, strings
|
||||||
|
|
||||||
# Stub get_resource_path so it finds the correct path while running tests
|
|
||||||
def get_resource_path(filename):
|
# # Stub get_resource_path so it finds the correct path while running tests
|
||||||
resources_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'share')
|
# def get_resource_path(filename):
|
||||||
path = os.path.join(resources_dir, filename)
|
# resources_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'share')
|
||||||
return path
|
# path = os.path.join(resources_dir, filename)
|
||||||
common.get_resource_path = get_resource_path
|
# return path
|
||||||
|
# common.get_resource_path = get_resource_path
|
||||||
|
|
||||||
|
|
||||||
def test_starts_with_empty_strings():
|
def test_starts_with_empty_strings():
|
||||||
"""creates an empty strings dict by default"""
|
""" Creates an empty strings dict by default """
|
||||||
assert strings.strings == {}
|
assert strings.strings == {}
|
||||||
|
|
||||||
|
|
||||||
def test_load_strings_defaults_to_english():
|
def test_underscore_is_function():
|
||||||
"""load_strings() loads English by default"""
|
assert callable(strings._) and isinstance(strings._, types.FunctionType)
|
||||||
locale.getdefaultlocale = lambda: ('en_US', 'UTF-8')
|
|
||||||
strings.load_strings(common)
|
|
||||||
assert strings._('wait_for_hs') == "Waiting for HS to be ready:"
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_strings_loads_other_languages():
|
class TestLoadStrings:
|
||||||
"""load_strings() loads other languages in different locales"""
|
def test_load_strings_defaults_to_english(
|
||||||
locale.getdefaultlocale = lambda: ('fr_FR', 'UTF-8')
|
self, locale_en, sys_onionshare_dev_mode):
|
||||||
strings.load_strings(common, "fr")
|
""" load_strings() loads English by default """
|
||||||
assert strings._('wait_for_hs') == "En attente du HS:"
|
strings.load_strings(common)
|
||||||
|
assert strings._('wait_for_hs') == "Waiting for HS to be ready:"
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_strings_loads_other_languages(
|
||||||
|
self, locale_fr, sys_onionshare_dev_mode):
|
||||||
|
""" load_strings() loads other languages in different locales """
|
||||||
|
strings.load_strings(common, "fr")
|
||||||
|
assert strings._('wait_for_hs') == "En attente du HS:"
|
||||||
|
|
||||||
|
def test_load_partial_strings(
|
||||||
|
self, locale_ru, sys_onionshare_dev_mode):
|
||||||
|
strings.load_strings(common)
|
||||||
|
assert strings._("give_this_url") == (
|
||||||
|
"Отправьте эту ссылку тому человеку, "
|
||||||
|
"которому вы хотите передать файл:")
|
||||||
|
assert strings._('wait_for_hs') == "Waiting for HS to be ready:"
|
||||||
|
|
||||||
|
def test_load_invalid_locale(
|
||||||
|
self, locale_invalid, sys_onionshare_dev_mode):
|
||||||
|
""" load_strings() raises a KeyError for an invalid locale """
|
||||||
|
with pytest.raises(KeyError):
|
||||||
|
strings.load_strings(common, 'XX')
|
||||||
|
@ -16,3 +16,69 @@ GNU General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from onionshare import OnionShare
|
||||||
|
|
||||||
|
|
||||||
|
class MyOnion:
|
||||||
|
def __init__(self, stealth=False):
|
||||||
|
self.auth_string = 'TestHidServAuth'
|
||||||
|
self.stealth = stealth
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def start_onion_service(_):
|
||||||
|
return 'test_service_id.onion'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def onionshare_obj():
|
||||||
|
return OnionShare(MyOnion())
|
||||||
|
|
||||||
|
|
||||||
|
class TestOnionShare:
|
||||||
|
def test_init(self, onionshare_obj):
|
||||||
|
assert onionshare_obj.hidserv_dir is None
|
||||||
|
assert onionshare_obj.onion_host is None
|
||||||
|
assert onionshare_obj.stealth is None
|
||||||
|
assert onionshare_obj.cleanup_filenames == []
|
||||||
|
assert onionshare_obj.local_only is False
|
||||||
|
assert onionshare_obj.stay_open is False
|
||||||
|
|
||||||
|
def test_set_stealth_true(self, onionshare_obj):
|
||||||
|
onionshare_obj.set_stealth(True)
|
||||||
|
assert onionshare_obj.stealth is True
|
||||||
|
assert onionshare_obj.onion.stealth is True
|
||||||
|
|
||||||
|
def test_set_stealth_false(self, onionshare_obj):
|
||||||
|
onionshare_obj.set_stealth(False)
|
||||||
|
assert onionshare_obj.stealth is False
|
||||||
|
assert onionshare_obj.onion.stealth is False
|
||||||
|
|
||||||
|
def test_start_onion_service(self, onionshare_obj):
|
||||||
|
onionshare_obj.set_stealth(False)
|
||||||
|
onionshare_obj.start_onion_service()
|
||||||
|
assert 17600 <= onionshare_obj.port <= 17650
|
||||||
|
assert onionshare_obj.onion_host == 'test_service_id.onion'
|
||||||
|
|
||||||
|
def test_start_onion_service_stealth(self, onionshare_obj):
|
||||||
|
onionshare_obj.set_stealth(True)
|
||||||
|
onionshare_obj.start_onion_service()
|
||||||
|
assert onionshare_obj.auth_string == 'TestHidServAuth'
|
||||||
|
|
||||||
|
def test_start_onion_service_local_only(self, onionshare_obj):
|
||||||
|
onionshare_obj.local_only = True
|
||||||
|
onionshare_obj.start_onion_service()
|
||||||
|
assert onionshare_obj.onion_host == '127.0.0.1:{}'.format(
|
||||||
|
onionshare_obj.port)
|
||||||
|
|
||||||
|
def test_cleanup(self, onionshare_obj, temp_dir_1024, temp_file_1024):
|
||||||
|
onionshare_obj.cleanup_filenames = [temp_dir_1024, temp_file_1024]
|
||||||
|
onionshare_obj.cleanup()
|
||||||
|
|
||||||
|
assert os.path.exists(temp_dir_1024) is False
|
||||||
|
assert os.path.exists(temp_dir_1024) is False
|
||||||
|
assert onionshare_obj.cleanup_filenames == []
|
||||||
|
Loading…
Reference in New Issue
Block a user