Skip to content

Commit 8b67e90

Browse files
committed
Fixed test compatibility issues, and added tests for the bread and bwrite functions
1 parent 490f33a commit 8b67e90

File tree

3 files changed

+231
-98
lines changed

3 files changed

+231
-98
lines changed

tests/bencode_tests.py

Lines changed: 151 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -3,103 +3,156 @@
33

44
"""bencode.py tests."""
55

6-
from collections import OrderedDict
7-
8-
from bencode import BTFailure, bencode, bdecode
9-
10-
import unittest
11-
12-
13-
class KnownValues(unittest.TestCase):
14-
"""
15-
Test known bencode values.
16-
17-
Example values partially taken from http://en.wikipedia.org/wiki/Bencode, test case inspired
18-
by Mark Pilgrim's examples: http://diveintopython.org/unit_testing/romantest.html
19-
"""
20-
21-
knownValues = (
22-
(0, 'i0e'.encode('utf-8')),
23-
(1, 'i1e'.encode('utf-8')),
24-
(10, 'i10e'.encode('utf-8')),
25-
(42, 'i42e'.encode('utf-8')),
26-
(-42, 'i-42e'.encode('utf-8')),
27-
(True, 'i1e'.encode('utf-8')),
28-
(False, 'i0e'.encode('utf-8')),
29-
('spam', '4:spam'.encode('utf-8')),
30-
('parrot sketch', '13:parrot sketch'.encode('utf-8')),
31-
(['parrot sketch', 42], 'l13:parrot sketchi42ee'.encode('utf-8')),
32-
({
33-
'foo': 42,
34-
'bar': 'spam'
35-
}, 'd3:bar4:spam3:fooi42ee'.encode('utf-8')),
36-
(OrderedDict((
37-
('bar', 'spam'),
38-
('foo', 42)
39-
)), 'd3:bar4:spam3:fooi42ee'.encode('utf-8')),
40-
)
41-
42-
def testBencodeKnownValues(self):
43-
"""Encode should give known result with known input."""
44-
for plain, encoded in self.knownValues:
45-
result = bencode(plain)
46-
self.assertEqual(encoded, result)
47-
48-
def testBdecodeKnownValues(self):
49-
"""Decode should give known result with known input."""
50-
for plain, encoded in self.knownValues:
51-
result = bdecode(encoded)
52-
self.assertEqual(plain, result)
53-
54-
def testRoundtripEncoded(self):
55-
"""Consecutive calls to decode and encode should deliver the original data again."""
56-
for plain, encoded in self.knownValues:
57-
result = bdecode(encoded)
58-
self.assertEqual(encoded, bencode(result))
59-
60-
def testRoundtripDecoded(self):
61-
"""Consecutive calls to encode and decode should deliver the original data again."""
62-
for plain, encoded in self.knownValues:
63-
result = bencode(plain)
64-
self.assertEqual(plain, bdecode(result))
65-
66-
67-
class IllegalValues(unittest.TestCase):
68-
"""Test handling of illegal values."""
6+
from bencode import Bencached, BTFailure, bencode, bdecode
697

8+
import pytest
9+
import sys
10+
11+
try:
12+
from collections import OrderedDict
13+
except ImportError:
14+
OrderedDict = None
15+
16+
17+
VALUES = [
18+
(0, 'i0e'),
19+
(1, 'i1e'),
20+
(10, 'i10e'),
21+
(42, 'i42e'),
22+
(-42, 'i-42e'),
23+
(True, 'i1e'),
24+
(False, 'i0e'),
25+
('spam', '4:spam'),
26+
('parrot sketch', '13:parrot sketch'),
27+
(['parrot sketch', 42], 'l13:parrot sketchi42ee'),
28+
({'foo': 42, 'bar': 'spam'}, 'd3:bar4:spam3:fooi42ee')
29+
]
30+
31+
if OrderedDict is not None:
32+
VALUES.append((OrderedDict((
33+
('bar', 'spam'),
34+
('foo', 42)
35+
)), 'd3:bar4:spam3:fooi42ee'))
36+
37+
38+
@pytest.mark.skipif(sys.version_info[0] < 3, reason="Requires: Python 3+")
39+
def test_encode():
40+
"""Encode should give known result with known input."""
41+
for plain, encoded in VALUES:
42+
assert encoded.encode('utf-8') == bencode(plain)
43+
44+
45+
@pytest.mark.skipif(sys.version_info[0] != 2, reason="Requires: Python 2")
46+
def test_encode_py2():
47+
"""Encode should give known result with known input."""
48+
for plain, encoded in VALUES:
49+
assert encoded == bencode(plain)
50+
51+
52+
@pytest.mark.skipif(sys.version_info[0] < 3, reason="Requires: Python 3+")
53+
def test_encode_bencached():
54+
"""Ensure Bencached objects can be encoded."""
55+
assert bencode([Bencached(bencode('test'))]) == b'l4:teste'
56+
57+
58+
@pytest.mark.skipif(sys.version_info[0] != 2, reason="Requires: Python 2")
59+
def test_encode_bencached_py2():
60+
"""Ensure Bencached objects can be encoded."""
61+
assert bencode([Bencached(bencode('test'))]) == 'l4:teste'
62+
63+
64+
def test_encode_bytes():
65+
"""Ensure bytes can be encoded."""
66+
assert bencode(b'\x9c') == b'1:\x9c'
67+
68+
69+
@pytest.mark.skipif(sys.version_info[0] < 3, reason="Requires: Python 3+")
70+
def test_decode():
71+
"""Decode should give known result with known input."""
72+
for plain, encoded in VALUES:
73+
assert plain == bdecode(encoded.encode('utf-8'))
74+
75+
76+
@pytest.mark.skipif(sys.version_info[0] != 2, reason="Requires: Python 2")
77+
def test_decode_py2():
78+
"""Decode should give known result with known input."""
79+
for plain, encoded in VALUES:
80+
assert plain == bdecode(encoded)
81+
82+
83+
def test_decode_bytes():
84+
"""Ensure bytes can be decoded."""
85+
assert bdecode(b'1:\x9c') == b'\x9c'
86+
87+
88+
@pytest.mark.skipif(sys.version_info[0] < 3, reason="Requires: Python 3+")
89+
def test_encode_roundtrip():
90+
"""Consecutive calls to decode and encode should deliver the original data again."""
91+
for plain, encoded in VALUES:
92+
assert encoded.encode('utf-8') == bencode(bdecode(encoded.encode('utf-8')))
93+
94+
95+
@pytest.mark.skipif(sys.version_info[0] != 2, reason="Requires: Python 2")
96+
def test_encode_roundtrip_py2():
97+
"""Consecutive calls to decode and encode should deliver the original data again."""
98+
for plain, encoded in VALUES:
99+
assert encoded == bencode(bdecode(encoded))
100+
101+
102+
def test_decode_roundtrip():
103+
"""Consecutive calls to encode and decode should deliver the original data again."""
104+
for plain, encoded in VALUES:
105+
assert plain == bdecode(bencode(plain))
106+
107+
108+
# TODO: BTL implementation currently chokes on this type of input
109+
# def test_encode_float_error(self):
110+
# """ floats cannot be encoded. """
111+
# self.assertRaises(BTFailure, bencode, 1.0)
112+
113+
def test_decode_parameter():
114+
"""Ensure non-strings raise an exception."""
70115
# TODO: BTL implementation currently chokes on this type of input
71-
# def testFloatRaisesIllegalForEncode(self):
72-
# """ floats cannot be encoded. """
73-
# self.assertRaises(BTFailure, bencode, 1.0)
74-
75-
def testNonStringsRaiseIllegalInputForDecode(self):
76-
"""Ensure non-strings raise an exception."""
77-
# TODO: BTL implementation currently chokes on this type of input
78-
# self.assertRaises(BTFailure, bdecode, 0)
79-
# self.assertRaises(BTFailure, bdecode, None)
80-
# self.assertRaises(BTFailure, bdecode, 1.0)
81-
self.assertRaises(BTFailure, bdecode, [1, 2])
82-
self.assertRaises(BTFailure, bdecode, {'foo': 'bar'})
83-
84-
def testRaiseIllegalInputForDecode(self):
85-
"""Illegally formatted strings should raise an exception when decoded."""
86-
self.assertRaises(BTFailure, bdecode, "foo")
87-
self.assertRaises(BTFailure, bdecode, "x:foo")
88-
self.assertRaises(BTFailure, bdecode, "x42e")
89-
90-
91-
class Dictionaries(unittest.TestCase):
92-
"""Test handling of dictionaries."""
93-
94-
def testSortedKeysForDicts(self):
95-
"""Ensure the keys of a dictionary are sorted before being encoded."""
96-
encoded = bencode({'zoo': 42, 'bar': 'spam'})
97-
98-
self.assertTrue(encoded.index(b'zoo') > encoded.index(b'bar'))
99-
100-
def testNestedDictionary(self):
101-
"""Test the handling of nested dicts."""
102-
self.assertEqual(
103-
bencode({'foo': 42, 'bar': {'sketch': 'parrot', 'foobar': 23}}),
104-
'd3:bard6:foobari23e6:sketch6:parrote3:fooi42ee'.encode('utf-8')
105-
)
116+
# self.assertRaises(BTFailure, bdecode, 0)
117+
# self.assertRaises(BTFailure, bdecode, None)
118+
# self.assertRaises(BTFailure, bdecode, 1.0)
119+
with pytest.raises(BTFailure):
120+
bdecode([1, 2])
121+
122+
with pytest.raises(BTFailure):
123+
bdecode({'foo': 'bar'})
124+
125+
126+
def test_decode_errors():
127+
"""Illegally formatted strings should raise an exception when decoded."""
128+
with pytest.raises(BTFailure):
129+
bdecode("foo")
130+
131+
with pytest.raises(BTFailure):
132+
bdecode("x:foo")
133+
134+
with pytest.raises(BTFailure):
135+
bdecode("x42e")
136+
137+
138+
def test_dictionary_sorted():
139+
"""Ensure the keys of a dictionary are sorted before being encoded."""
140+
encoded = bencode({'zoo': 42, 'bar': 'spam'})
141+
142+
assert encoded.index(b'zoo') > encoded.index(b'bar')
143+
144+
145+
@pytest.mark.skipif(sys.version_info[0] < 3, reason="Requires: Python 3+")
146+
def test_dictionary_nested():
147+
"""Test the handling of nested dictionaries."""
148+
encoded = bencode({'foo': 42, 'bar': {'sketch': 'parrot', 'foobar': 23}})
149+
150+
assert encoded == 'd3:bard6:foobari23e6:sketch6:parrote3:fooi42ee'.encode('utf-8')
151+
152+
153+
@pytest.mark.skipif(sys.version_info[0] != 2, reason="Requires: Python 2")
154+
def test_dictionary_nested_py2():
155+
"""Test the handling of nested dictionaries."""
156+
encoded = bencode({'foo': 42, 'bar': {'sketch': 'parrot', 'foobar': 23}})
157+
158+
assert encoded == 'd3:bard6:foobari23e6:sketch6:parrote3:fooi42ee'

tests/file_tests.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/usr/bin/env python
2+
# encoding: utf-8
3+
4+
"""bencode.py - file tests."""
5+
6+
from bencode import bread, bwrite
7+
8+
import os
9+
import pytest
10+
import sys
11+
12+
FIXTURE_DIR = os.path.join(os.path.dirname(__file__), 'fixtures')
13+
TEMP_DIR = os.path.join(os.path.dirname(__file__), '.tmp')
14+
15+
# Ensure temp directory exists
16+
if not os.path.exists(TEMP_DIR):
17+
os.makedirs(TEMP_DIR)
18+
19+
20+
def test_read_file():
21+
"""Test the reading of bencode files."""
22+
with open(os.path.join(FIXTURE_DIR, 'alpha'), 'rb') as fp:
23+
data = bread(fp)
24+
25+
assert data == {'foo': 42, 'bar': {'sketch': 'parrot', 'foobar': 23}}
26+
27+
28+
def test_read_path():
29+
"""Test the reading of bencode paths."""
30+
data = bread(os.path.join(FIXTURE_DIR, 'alpha'))
31+
32+
assert data == {'foo': 42, 'bar': {'sketch': 'parrot', 'foobar': 23}}
33+
34+
35+
@pytest.mark.skipif(sys.version_info < (3, 4), reason="Requires: Python 3.4+")
36+
def test_read_pathlib():
37+
"""Test the reading of bencode paths."""
38+
from pathlib import Path
39+
40+
data = bread(Path(FIXTURE_DIR, 'alpha'))
41+
42+
assert data == {'foo': 42, 'bar': {'sketch': 'parrot', 'foobar': 23}}
43+
44+
45+
def test_write_file():
46+
"""Test the writing of bencode paths."""
47+
with open(os.path.join(TEMP_DIR, 'beta'), 'wb') as fp:
48+
bwrite(
49+
{'foo': 42, 'bar': {'sketch': 'parrot', 'foobar': 23}},
50+
fp
51+
)
52+
53+
with open(os.path.join(TEMP_DIR, 'beta'), 'r') as fp:
54+
assert fp.read() == 'd3:bard6:foobari23e6:sketch6:parrote3:fooi42ee'
55+
56+
57+
def test_write_path():
58+
"""Test the writing of bencode files."""
59+
bwrite(
60+
{'foo': 42, 'bar': {'sketch': 'parrot', 'foobar': 23}},
61+
os.path.join(TEMP_DIR, 'beta')
62+
)
63+
64+
with open(os.path.join(TEMP_DIR, 'beta'), 'r') as fp:
65+
assert fp.read() == 'd3:bard6:foobari23e6:sketch6:parrote3:fooi42ee'
66+
67+
68+
@pytest.mark.skipif(sys.version_info < (3, 4), reason="Requires: Python 3.4+")
69+
def test_write_pathlib():
70+
"""Test the reading of bencode paths."""
71+
from pathlib import Path
72+
73+
bwrite(
74+
{'foo': 42, 'bar': {'sketch': 'parrot', 'foobar': 23}},
75+
Path(TEMP_DIR, 'beta')
76+
)
77+
78+
with open(os.path.join(TEMP_DIR, 'beta'), 'r') as fp:
79+
assert fp.read() == 'd3:bard6:foobari23e6:sketch6:parrote3:fooi42ee'

tests/fixtures/alpha

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
d3:bard6:foobari23e6:sketch6:parrote3:fooi42ee

0 commit comments

Comments
 (0)