buku/tests/test_buku.py

716 lines
21 KiB
Python
Raw Normal View History

"""test module."""
from itertools import product
from unittest import mock
import json
import os
import signal
import sys
import unittest
import pytest
from buku import is_int, parse_tags, prep_tag_search
only_python_3_5 = pytest.mark.skipif(
sys.version_info < (3, 5), reason="requires Python 3.5 or later")
@pytest.mark.parametrize(
'url, exp_res',
[
['http://example.com', False],
['ftp://ftp.somedomain.org', False],
['http://examplecom.', True],
['http://.example.com', True],
['http://example.com.', True],
['about:newtab', True],
['chrome://version/', True],
]
)
def test_is_bad_url(url, exp_res):
"""test func."""
import buku
res = buku.is_bad_url(url)
assert res == exp_res
@pytest.mark.parametrize(
'url, exp_res',
[
('http://example.com/file.pdf', True),
('http://example.com/file.txt', True),
('http://example.com/file.jpg', False),
]
)
def test_is_ignored_mime(url, exp_res):
"""test func."""
import buku
assert exp_res == buku.is_ignored_mime(url)
def test_get_page_title():
"""test func."""
resp = mock.Mock()
parser = mock.Mock()
with mock.patch('buku.BukuHTMLParser', return_value=parser):
import buku
res = buku.get_page_title(resp)
assert res == parser.parsed_title
def test_gen_headers():
"""test func."""
import buku
exp_myheaders = {
'Accept-Encoding': 'gzip,deflate',
'User-Agent': buku.USER_AGENT,
'Accept': '*/*',
'Cookie': '',
'DNT': '1'
}
buku.gen_headers()
assert buku.myproxy is None
assert buku.myheaders == exp_myheaders
@pytest.mark.parametrize('m_myproxy', [None, mock.Mock()])
def test_get_PoolManager(m_myproxy):
"""test func."""
with mock.patch('buku.urllib3') as m_ul3:
import buku
buku.myproxy = m_myproxy
res = buku.get_PoolManager()
if m_myproxy:
m_ul3.ProxyManager.assert_called_once_with(
m_myproxy, num_pools=1, headers=buku.myheaders)
assert res == m_ul3.ProxyManager.return_value
else:
m_ul3.PoolManager.assert_called_once_with(
num_pools=1, headers=buku.myheaders)
assert res == m_ul3.PoolManager.return_value
@pytest.mark.parametrize(
'keywords, exp_res',
[
(None, None),
([], None),
(['tag1', 'tag2'], ',tag1 tag2,'),
(['tag1,tag2', 'tag3'], ',tag1,tag2 tag3,'),
]
)
def test_parse_tags(keywords, exp_res):
"""test func."""
import buku
if keywords is None:
pass
elif not keywords:
exp_res = buku.DELIM
res = buku.parse_tags(keywords)
assert res == exp_res
@pytest.mark.parametrize(
'records, field_filter, exp_res',
[
[
[(1, 'http://url1.com', 'title1', ',tag1,'),
(2, 'http://url2.com', 'title2', ',tag1,tag2,')],
1,
['1\thttp://url1.com', '2\thttp://url2.com']
],
[
[(1, 'http://url1.com', 'title1', ',tag1,'),
(2, 'http://url2.com', 'title2', ',tag1,tag2,')],
2,
['1\thttp://url1.com\ttag1', '2\thttp://url2.com\ttag1,tag2']
],
[
[(1, 'http://url1.com', 'title1', ',tag1,'),
(2, 'http://url2.com', 'title2', ',tag1,tag2,')],
3,
['1\ttitle1', '2\ttitle2']
],
[
[(1, 'http://url1.com', 'title1', ',tag1,'),
(2, 'http://url2.com', 'title2', ',tag1,tag2,')],
4,
['1\thttp://url1.com\ttitle1\ttag1', '2\thttp://url2.com\ttitle2\ttag1,tag2']
],
[
[(1, 'http://url1.com', 'title1', ',tag1,'),
(2, 'http://url2.com', 'title2', ',tag1,tag2,')],
10,
['http://url1.com', 'http://url2.com']
],
[
[(1, 'http://url1.com', 'title1', ',tag1,'),
(2, 'http://url2.com', 'title2', ',tag1,tag2,')],
20,
['http://url1.com\ttag1', 'http://url2.com\ttag1,tag2']
],
[
[(1, 'http://url1.com', 'title1', ',tag1,'),
(2, 'http://url2.com', 'title2', ',tag1,tag2,')],
30,
['title1', 'title2']
],
[
[(1, 'http://url1.com', 'title1', ',tag1,'),
(2, 'http://url2.com', 'title2', ',tag1,tag2,')],
40,
['http://url1.com\ttitle1\ttag1', 'http://url2.com\ttitle2\ttag1,tag2']
]
]
)
def test_print_rec_with_filter(records, field_filter, exp_res):
"""test func."""
with mock.patch('buku.print', create=True) as m_print:
import buku
buku.print_rec_with_filter(records, field_filter)
for res in exp_res:
m_print.assert_any_call(res)
@pytest.mark.parametrize(
'taglist, exp_res',
[
[
'tag1, tag2+3',
([',tag1,', ',tag2+3,'], 'OR', None)
],
[
'tag1 + tag2-3 + tag4',
([',tag1,', ',tag2-3,', ',tag4,'], 'AND', None)
],
[
'tag1, tag2-3 - tag4, tag5',
([',tag1,', ',tag2-3,'], 'OR', ',tag4,|,tag5,')
]
]
)
def test_prep_tag_search(taglist, exp_res):
"""test prep_tag_search helper function"""
results = prep_tag_search(taglist)
assert results == exp_res
@pytest.mark.parametrize(
'nav, is_editor_valid_retval, edit_rec_retval',
product(
['w', [None, None, 1], [None, None, 'string']],
[True, False],
[[mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock()], None]
)
)
def test_edit_at_prompt(nav, is_editor_valid_retval, edit_rec_retval):
"""test func."""
obj = mock.Mock()
editor = mock.Mock()
with mock.patch('buku.get_system_editor', return_value=editor), \
mock.patch('buku.is_editor_valid', return_value=is_editor_valid_retval), \
mock.patch('buku.edit_rec', return_value=edit_rec_retval) as m_edit_rec:
import buku
buku.edit_at_prompt(obj, nav)
# test
if nav == 'w' and not is_editor_valid_retval:
return
elif nav == 'w':
m_edit_rec.assert_called_once_with(editor, '', None, buku.DELIM, None)
elif buku.is_int(nav[2:]):
obj.edit_update_rec.assert_called_once_with(int(nav[2:]))
return
else:
editor = nav[2:]
m_edit_rec.assert_called_once_with(editor, '', None, buku.DELIM, None)
if edit_rec_retval is not None:
obj.add_rec(*edit_rec_retval)
@pytest.mark.parametrize(
'field_filter, single_record',
product(list(range(4)), [True, False])
)
def test_format_json(field_filter, single_record):
"""test func."""
resultset = [['row{}'.format(x) for x in range(5)]]
if field_filter == 1:
marks = {'uri': 'row1'}
elif field_filter == 2:
marks = {'uri': 'row1', 'tags': 'row3'[1:-1]}
elif field_filter == 3:
marks = {'title': 'row2'}
else:
marks = {
'index': 'row0',
'uri': 'row1',
'title': 'row2',
'description': 'row4',
'tags': 'row3'[1:-1]
}
if not single_record:
marks = [marks]
with mock.patch('buku.json') as m_json:
import buku
res = buku.format_json(resultset, single_record, field_filter)
m_json.dumps.assert_called_once_with(marks, sort_keys=True, indent=4)
assert res == m_json.dumps.return_value
@pytest.mark.parametrize(
'string, exp_res',
[
('string', False),
('12', True),
('12.1', False),
]
)
def test_is_int(string, exp_res):
"""test func."""
import buku
assert exp_res == buku.is_int(string)
@pytest.mark.parametrize(
'url, opened_url, platform',
[
['http://example.com', 'http://example.com', 'linux'],
['example.com', 'http://example.com', 'linux'],
['http://example.com', 'http://example.com', 'win32'],
]
)
def test_browse(url, opened_url, platform):
"""test func."""
with mock.patch('buku.webbrowser') as m_webbrowser, \
2018-02-21 01:09:30 -06:00
mock.patch('buku.sys') as m_sys, \
mock.patch('buku.os'):
m_sys.platform = platform
2018-02-21 01:09:30 -06:00
get_func_retval = mock.Mock()
m_webbrowser.get.return_value = get_func_retval
import buku
2017-09-08 14:51:35 -05:00
buku.browse.suppress_browser_output = True
2018-02-18 11:44:00 -06:00
buku.browse.override_text_browser = False
buku.browse(url)
2018-02-21 01:09:30 -06:00
if platform == 'win32':
m_webbrowser.open.assert_called_once_with(opened_url, new=2)
else:
get_func_retval.open.assert_called_once_with(opened_url, new=2)
@only_python_3_5
@pytest.mark.parametrize(
'status_code, latest_release',
product([200, 404], [True, False])
)
def test_check_upstream_release(status_code, latest_release):
"""test func."""
resp = mock.Mock()
resp.status = status_code
m_manager = mock.Mock()
m_manager.request.return_value = resp
with mock.patch('buku.urllib3') as m_urllib3, \
mock.patch('buku.print') as m_print:
import buku
if latest_release:
latest_version = 'v{}'.format(buku.__version__)
else:
latest_version = 'v0'
m_urllib3.PoolManager.return_value = m_manager
resp.data.decode.return_value = json.dumps([{'tag_name': latest_version}])
buku.check_upstream_release()
if status_code != 200:
return
len(m_print.mock_calls) == 1
@pytest.mark.parametrize(
'exp, item, exp_res',
[
('cat.y', 'catty', True),
('cat.y', 'caty', False),
]
)
def test_regexp(exp, item, exp_res):
"""test func."""
import buku
res = buku.regexp(exp, item)
assert res == exp_res
@pytest.mark.parametrize('token, exp_res', [('text', ',text,')])
def test_delim_wrap(token, exp_res):
"""test func."""
import buku
res = buku.delim_wrap(token)
assert res == exp_res
@only_python_3_5
def test_read_in():
"""test func."""
message = mock.Mock()
with mock.patch('buku.disable_sigint_handler'), \
mock.patch('buku.enable_sigint_handler'), \
mock.patch('buku.input', return_value=message):
import buku
res = buku.read_in(msg=mock.Mock())
assert res == message
def test_sigint_handler_with_mock():
"""test func."""
with mock.patch('buku.os') as m_os:
import buku
buku.sigint_handler(mock.Mock(), mock.Mock())
m_os._exit.assert_called_once_with(1)
def test_get_system_editor():
"""test func."""
with mock.patch('buku.os') as m_os:
import buku
res = buku.get_system_editor()
assert res == m_os.environ.get.return_value
m_os.environ.get.assert_called_once_with('EDITOR', 'none')
@pytest.mark.parametrize(
'editor, exp_res',
[
('none', False),
('0', False),
('random_editor', True),
]
)
def test_is_editor_valid(editor, exp_res):
"""test func."""
import buku
assert buku.is_editor_valid(editor) == exp_res
@pytest.mark.parametrize(
'url, title_in, tags_in, desc',
product(
[None, 'example.com'],
[None, '', 'title'],
['', 'tag1,tag2', ',tag1,tag2,'],
[None, '', 'description'],
)
)
def test_to_temp_file_content(url, title_in, tags_in, desc):
"""test func."""
import buku
res = buku.to_temp_file_content(url, title_in, tags_in, desc)
lines = [
'# Lines beginning with "#" will be stripped.',
'# Add URL in next line (single line).',
'# Add TITLE in next line (single line). Leave blank to web fetch, "-" for no title.',
'# Add comma-separated TAGS in next line (single line).',
'# Add COMMENTS in next line(s).',
]
idx_offset = 0
# url
if url is not None:
lines.insert(2, url)
idx_offset += 1
if title_in is None:
title_in = ''
elif title_in == '':
title_in = '-'
else:
pass
# title
lines.insert(idx_offset + 3, title_in)
idx_offset += 1
# tags
lines.insert(idx_offset + 4, tags_in.strip(buku.DELIM))
idx_offset += 1
# description
if desc is not None and desc != '':
pass
else:
desc = ''
lines.insert(idx_offset + 5, desc)
for idx, res_line in enumerate(res.splitlines()):
assert lines[idx] == res_line
@pytest.mark.parametrize(
'content, exp_res',
[
('', None),
('#line1\n#line2', None),
(
'\n'.join([
'example.com',
'title',
'tags',
'desc',
]),
('example.com', 'title', ',tags,', 'desc')
)
]
)
def test_parse_temp_file_content(content, exp_res):
"""test func."""
import buku
res = buku.parse_temp_file_content(content)
assert res == exp_res
@only_python_3_5
@pytest.mark.skip(reason="can't patch subprocess")
def test_edit_rec():
"""test func."""
editor = 'nanoe'
args = ('url', 'title_in', 'tags_in', 'desc')
with mock.patch('buku.to_temp_file_content'), \
mock.patch('buku.os'), \
mock.patch('buku.open'), \
mock.patch('buku.parse_temp_file_content') as m_ptfc:
import buku
res = buku.edit_rec(editor, *args)
assert res == m_ptfc.return_value
@pytest.mark.parametrize('argv, pipeargs, isatty', product(['argv'], [None, []], [True, False]))
def test_piped_input(argv, pipeargs, isatty):
"""test func."""
with mock.patch('buku.sys') as m_sys:
m_sys.stdin.isatty.return_value = isatty
m_sys.stdin.readlines.return_value = 'arg1\narg2'
import buku
if pipeargs is None and not isatty:
with pytest.raises(TypeError):
buku.piped_input(argv, pipeargs)
return
buku.piped_input(argv, pipeargs)
class TestHelpers(unittest.TestCase):
# @unittest.skip('skipping')
def test_parse_tags(self):
# call with None
parsed = parse_tags(None)
self.assertIsNone(parsed)
# call with empty list
parsed = parse_tags([])
self.assertEqual(parsed, ",")
# empty tags
parsed = parse_tags([",,,,,"])
self.assertEqual(parsed, ",")
# sorting tags
parsed = parse_tags(["z_tag,a_tag,n_tag"])
self.assertEqual(parsed, ",a_tag,n_tag,z_tag,")
# whitespaces
parsed = parse_tags([" a tag , , , ,\t,\n,\r,\x0b,\x0c"])
self.assertEqual(parsed, ",a tag,")
# duplicates, excessive spaces
parsed = parse_tags(["tag,tag, tag, tag,tag , tag "])
self.assertEqual(parsed, ",tag,")
# escaping quotes
parsed = parse_tags(["\"tag\",\'tag\',tag"])
self.assertEqual(parsed, ",\"tag\",\'tag\',tag,")
# combo
parsed = parse_tags([",,z_tag, a tag ,\t,,, ,n_tag ,n_tag, a_tag, \na tag ,\r, \"a_tag\""])
self.assertEqual(parsed, ",\"a_tag\",a tag,a_tag,n_tag,z_tag,")
# @unittest.skip('skipping')
def test_is_int(self):
self.assertTrue(is_int('0'))
self.assertTrue(is_int('1'))
self.assertTrue(is_int('-1'))
self.assertFalse(is_int(''))
self.assertFalse(is_int('one'))
# This test fails because we use os._exit() now
@unittest.skip('skipping')
def test_sigint_handler(capsys):
try:
# sending SIGINT to self
os.kill(os.getpid(), signal.SIGINT)
except SystemExit as error:
out, err = capsys.readouterr()
# assert exited with 1
assert error.args[0] == 1
# assert proper error message
assert out == ''
assert err == "\nInterrupted.\n"
@pytest.mark.parametrize(
'url, exp_res',
[
['http://example.com.', ('', 0, 1)],
['http://example.com', ('Example Domain', 0, 0)],
['http://example.com/page1.txt', (('', 1, 0))],
['about:new_page', (('', 0, 1))],
['chrome://version/', (('', 0, 1))],
]
)
def test_network_handler_with_url(url, exp_res):
"""test func."""
import buku
import urllib3
buku.urllib3 = urllib3
buku.myproxy = None
res = buku.network_handler(url)
assert res == exp_res
@pytest.mark.parametrize(
'url, exp_res',
[
('http://example.com', False),
('apt:package1,package2,package3', True),
('apt://firefox', True),
('file:///tmp/vim-markdown-preview.html', True),
('place:sort=8&maxResults=10', True),
]
)
def test_is_nongeneric_url(url, exp_res):
import buku
res = buku.is_nongeneric_url(url)
assert res == exp_res
@pytest.mark.parametrize(
'newtag, exp_res',
[
(None, ('http://example.com', 'text1', None, None, 0, True)),
('tag1',('http://example.com', 'text1', ',tag1,', None, 0, True)),
]
)
def test_import_md(tmpdir, newtag, exp_res):
from buku import import_md
p = tmpdir.mkdir("importmd").join("test.md")
p.write("[text1](http://example.com)")
res = list(import_md(p.strpath, newtag))
assert res[0] == exp_res
@pytest.mark.parametrize(
'html_text, exp_res',
[
(
"""<DT><A HREF="https://github.com/j" ADD_DATE="1360951967" PRIVATE="1" TAGS="tag1,tag2">GitHub</A>
<DD>comment for the bookmark here
<a> </a>""",
((
'https://github.com/j', 'GitHub', ',tag1,tag2,',
'comment for the bookmark here\n', 0, True
),)
),
(
"""DT><A HREF="https://github.com/j" ADD_DATE="1360951967" PRIVATE="1" TAGS="tag1,tag2">GitHub</A>
<DD>comment for the bookmark here
<a>second line of the comment here</a>""",
((
'https://github.com/j', 'GitHub', ',tag1,tag2,',
'comment for the bookmark here\n ', 0, True
),)
),
(
"""DT><A HREF="https://github.com/j" ADD_DATE="1360951967" PRIVATE="1" TAGS="tag1,tag2">GitHub</A>
<DD>comment for the bookmark here
second line of the comment here
third line of the comment here
<DT><A HREF="https://news.com/" ADD_DATE="1360951967" PRIVATE="1" TAGS="tag1,tag2,tag3">News</A>""",
(
(
'https://github.com/j', 'GitHub', ',tag1,tag2,',
'comment for the bookmark here\n '
'second line of the comment here\n '
'third line of the comment here\n ',
0, True
),
('https://news.com/', 'News', ',tag1,tag2,tag3,', None, 0, True)
)
),
(
"""DT><A HREF="https://github.com/j" ADD_DATE="1360951967" PRIVATE="1" TAGS="tag1,tag2">GitHub</A>
<DD>comment for the bookmark here""",
((
'https://github.com/j', 'GitHub', ',tag1,tag2,',
'comment for the bookmark here', 0, True
),)
)
]
)
def test_import_html(html_text, exp_res):
"""test method."""
from buku import import_html
from bs4 import BeautifulSoup
html_soup = BeautifulSoup(html_text, 'html.parser')
res = list(import_html(html_soup, False, None))
for item, exp_item in zip(res, exp_res):
assert item == exp_item
def test_import_html_and_add_parent():
from buku import import_html
from bs4 import BeautifulSoup
html_text = """<DT><H3>1s</H3>
<DL><p>
<DT><A HREF="http://example.com/"></A>"""
exp_res = ('http://example.com/', None, ',1s,', None, 0, True)
html_soup = BeautifulSoup(html_text, 'html.parser')
res = list(import_html(html_soup, True, None))
assert res[0] == exp_res
def test_import_html_and_new_tag():
from buku import import_html
from bs4 import BeautifulSoup
html_text = """<DT><A HREF="https://github.com/j" TAGS="tag1,tag2">GitHub</A>
<DD>comment for the bookmark here"""
exp_res = (
'https://github.com/j', 'GitHub', ',tag1,tag2,tag3,',
'comment for the bookmark here', 0, True
)
html_soup = BeautifulSoup(html_text, 'html.parser')
res = list(import_html(html_soup, False, 'tag3'))
assert res[0] == exp_res
@pytest.mark.parametrize(
'platform, params',
[
['linux', ['xsel', '-b', '-i']],
['freebsd', ['xsel', '-b', '-i']],
['openbsd', ['xsel', '-b', '-i']],
['darwin', ['pbcopy']],
['win32', ['clip']],
['random', None],
],
)
def test_copy_to_clipboard(platform, params):
# m_popen = mock.Mock()
content = mock.Mock()
m_popen_retval = mock.Mock()
platform_recognized = \
platform.startswith(('linux', 'freebsd', 'openbsd')) \
or platform in ('darwin', 'win32')
with mock.patch('buku.sys') as m_sys, \
mock.patch('buku.Popen', return_value=m_popen_retval) as m_popen, \
mock.patch('buku.shutil.which', return_value=True):
m_sys.platform = platform
from buku import copy_to_clipboard
import subprocess
copy_to_clipboard(content)
if platform_recognized:
m_popen.assert_called_once_with(
params, stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
m_popen_retval.communicate.assert_called_once_with(content)
else:
m_popen.assert_not_called()