bencode_blatyo 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +34 -0
- data/.document +5 -0
- data/.gitignore +25 -0
- data/LICENSE +20 -0
- data/README.rdoc +139 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/autotest/discover.rb +3 -0
- data/bencode_blatyo.gemspec +77 -0
- data/lib/bencode/dictionary.rb +57 -0
- data/lib/bencode/integer.rb +57 -0
- data/lib/bencode/list.rb +55 -0
- data/lib/bencode/parser.rb +113 -0
- data/lib/bencode/string.rb +58 -0
- data/lib/bencode.rb +56 -0
- data/spec/bencode/dictionary_spec.rb +79 -0
- data/spec/bencode/integer_spec.rb +72 -0
- data/spec/bencode/list_spec.rb +36 -0
- data/spec/bencode/parser_spec.rb +176 -0
- data/spec/bencode/string_spec.rb +73 -0
- data/spec/bencode_spec.rb +72 -0
- data/spec/samples/bencode.rb.torrent +1 -0
- data/spec/samples/mini.bencode +1 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +11 -0
- metadata +105 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
|
|
5
|
+
module BEncode
|
|
6
|
+
module String
|
|
7
|
+
module Generic
|
|
8
|
+
module InstanceMethods
|
|
9
|
+
# Encodes object into a bencoded string. BEncoded strings are length-prefixed base ten followed by a colon and
|
|
10
|
+
# the string.
|
|
11
|
+
#
|
|
12
|
+
# :symbol.bencode #=> "6:symbol"
|
|
13
|
+
#
|
|
14
|
+
# @return [String] the bencoded string
|
|
15
|
+
def bencode
|
|
16
|
+
(respond_to?(:to_s) ? to_s : to_str).bencode
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Registers a class as an object that can be converted into a bencoded string. Class must have instance method to_s
|
|
22
|
+
# or to_str.
|
|
23
|
+
#
|
|
24
|
+
# class MyClass
|
|
25
|
+
# def to_s
|
|
26
|
+
# "string"
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# BEncode::String.register MyClass
|
|
31
|
+
# my_class = MyClass.new
|
|
32
|
+
# my_class.bencode #=> "6:string"
|
|
33
|
+
#
|
|
34
|
+
# @param [Class#to_s, Class#to_str] type the class to add the bencode instance method to
|
|
35
|
+
def self.register(type)
|
|
36
|
+
type.send :include, Generic::InstanceMethods
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
register Symbol
|
|
40
|
+
register URI::Generic
|
|
41
|
+
|
|
42
|
+
module String
|
|
43
|
+
module InstanceMethods
|
|
44
|
+
# Encodes a string into a bencoded string. BEncoded strings are length-prefixed base ten followed by a colon and
|
|
45
|
+
# the string.
|
|
46
|
+
#
|
|
47
|
+
# "string".bencode #=> "6:string"
|
|
48
|
+
#
|
|
49
|
+
# @return [String] the bencoded string
|
|
50
|
+
def bencode
|
|
51
|
+
[length, ':', self].join
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
::String.send :include, InstanceMethods
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/bencode.rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
path = File.expand_path(File.dirname(__FILE__)) + "/bencode"
|
|
4
|
+
|
|
5
|
+
require path + "/string"
|
|
6
|
+
require path + "/integer"
|
|
7
|
+
require path + "/list"
|
|
8
|
+
require path + "/dictionary"
|
|
9
|
+
require path + "/parser"
|
|
10
|
+
|
|
11
|
+
module BEncode
|
|
12
|
+
class BEncodeError < StandardError; end
|
|
13
|
+
|
|
14
|
+
class << self
|
|
15
|
+
# This method decodes a bencoded string.
|
|
16
|
+
#
|
|
17
|
+
# BEncode.decode("6:string") #=> "string"
|
|
18
|
+
#
|
|
19
|
+
# @param [String] string the bencoded string to decode
|
|
20
|
+
# @return [String, Integer, Hash, Array] the decoded object
|
|
21
|
+
def decode(string)
|
|
22
|
+
scanner = StringScanner.new(string)
|
|
23
|
+
Parser.parse_object(scanner) or raise BEncodeError, "Invalid bencoding"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# This method decodes a bencoded file.
|
|
27
|
+
#
|
|
28
|
+
# BEncode.decode_file("simple.torrent") #=> "d8:announce32:http://www..."
|
|
29
|
+
#
|
|
30
|
+
# @param [String] file the file to decode
|
|
31
|
+
# @return [String, Integer, Hash, Array] the decoded object
|
|
32
|
+
def decode_file(file)
|
|
33
|
+
decode(File.open(file, 'rb') {|f| f.read})
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# This method encodes a bencoded object.
|
|
37
|
+
#
|
|
38
|
+
# BEncode.encode("string") #=> "6:string"
|
|
39
|
+
#
|
|
40
|
+
# @param [#bencode] object the object to encode
|
|
41
|
+
# @return [String] the bencoded object
|
|
42
|
+
def encode(object)
|
|
43
|
+
object.bencode
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# This method encodes a bencoded object.
|
|
47
|
+
#
|
|
48
|
+
# BEncode.encode("string") #=> "6:string"
|
|
49
|
+
#
|
|
50
|
+
# @param [String] file the file to write the bencoded object to
|
|
51
|
+
# @param [#bencode] object the object to encode
|
|
52
|
+
def encode_file(file, object)
|
|
53
|
+
File.open(file, 'wb') {|f| f.write encode(object)}
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require "spec"
|
|
4
|
+
require "spec_helper"
|
|
5
|
+
|
|
6
|
+
describe Hash do
|
|
7
|
+
describe "#bencode" do
|
|
8
|
+
it "should encode an empty hash" do
|
|
9
|
+
{}.bencode.should == "de"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context "a key should always be encoded as a string" do
|
|
13
|
+
it "should encode a string key as a string" do
|
|
14
|
+
{"string" => "string"}.bencode.should == "d6:string6:stringe"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should encode a symbol key as a string" do
|
|
18
|
+
{:symbol => :symbol}.bencode.should == "d6:symbol6:symbole"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should encode a uri key as a string" do
|
|
22
|
+
uri = URI.parse("http://github.com/blatyo/bencode")
|
|
23
|
+
{uri => uri}.bencode.should == "d32:http://github.com/blatyo/bencode32:http://github.com/blatyo/bencodee"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "should encode an integer key as a string" do
|
|
27
|
+
{1 => 1}.bencode.should == "d1:1i1ee"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should encode a float key as a string" do
|
|
31
|
+
{1.1 => 1.1}.bencode.should == "d3:1.1i1ee"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "should encode a time key as a string" do
|
|
35
|
+
time = Time.utc(0)
|
|
36
|
+
{time => time}.bencode.should == "d23:2000-01-01 00:00:00 UTCi946684800ee"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should encode an array key as a string" do
|
|
40
|
+
array = (1..4).to_a
|
|
41
|
+
{array => array}.bencode.should == "d12:[1, 2, 3, 4]li1ei2ei3ei4eee"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "should encode a hash key as a string" do
|
|
45
|
+
{{} => {}}.bencode.should == "d2:{}dee"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "should encode keys in sorted (as raw strings) order" do
|
|
50
|
+
{:a => 1, "A" => 1, 1=> 1}.bencode.should == "d1:1i1e1:Ai1e1:ai1ee"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe BEncode::Dictionary do
|
|
56
|
+
describe "#register" do
|
|
57
|
+
context "once an object has been registered as a BEncode dictionary" do
|
|
58
|
+
before :all do
|
|
59
|
+
klass = Class.new do
|
|
60
|
+
def to_h
|
|
61
|
+
{:a => "a", :b => "b"}
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
BEncode::Dictionary.register klass
|
|
65
|
+
@instance = klass.new
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
context "an instance of that object" do
|
|
69
|
+
it "should respond to bencode" do
|
|
70
|
+
@instance.should respond_to :bencode
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should encode to a bencoded dictionary" do
|
|
74
|
+
@instance.bencode.should == "d1:a1:a1:b1:be"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require "spec"
|
|
4
|
+
require "spec_helper"
|
|
5
|
+
|
|
6
|
+
describe Integer do
|
|
7
|
+
describe "#bencode" do
|
|
8
|
+
it "should encode a positive integer" do
|
|
9
|
+
1.bencode.should == "i1e"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "should encode a negative integer" do
|
|
13
|
+
-1.bencode.should == "i-1e"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should encode a positive big integer" do
|
|
17
|
+
10_000_000_000.bencode.should == "i10000000000e"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should encode a negative big integer" do
|
|
21
|
+
-10_000_000_000.bencode.should == "i-10000000000e"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe Numeric do
|
|
27
|
+
describe "#bencode" do
|
|
28
|
+
it "should encode a positive float with precision loss" do
|
|
29
|
+
1.1.bencode.should == "i1e"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should encode a negative float with precision loss" do
|
|
33
|
+
-1.1.bencode.should == "i-1e"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "should encode an positive exponential float" do
|
|
37
|
+
1e10.bencode.should == "i10000000000e"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should encode an negative exponential float" do
|
|
41
|
+
-1e10.bencode.should == "i-10000000000e"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe Time do
|
|
47
|
+
describe "#bencode" do
|
|
48
|
+
it "should encode to bencoding" do
|
|
49
|
+
Time.at(4).bencode.should == "i4e"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe BEncode::Integer do
|
|
55
|
+
describe "#register" do
|
|
56
|
+
context "once an object has been registered as a BEncode integer" do
|
|
57
|
+
before :all do
|
|
58
|
+
BEncode::Integer.register NilClass
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
context "an instance of that object" do
|
|
62
|
+
it "should respond to bencode" do
|
|
63
|
+
nil.should respond_to :bencode
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should encode to a bencoded integer" do
|
|
67
|
+
nil.bencode.should == "i0e"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require "spec"
|
|
4
|
+
require "spec_helper"
|
|
5
|
+
|
|
6
|
+
describe Array do
|
|
7
|
+
describe "#bencode" do
|
|
8
|
+
it "should encode an empty array" do
|
|
9
|
+
[].bencode.should == "le"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "should encode an array filled with bencodable objects" do
|
|
13
|
+
[:e, "a", 1, Time.at(11)].bencode.should == "l1:e1:ai1ei11ee"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe BEncode::List do
|
|
19
|
+
describe "#register" do
|
|
20
|
+
context "once an object has been registered as a BEncode list" do
|
|
21
|
+
before :all do
|
|
22
|
+
BEncode::List.register Range
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context "an instance of that object" do
|
|
26
|
+
it "should respond to bencode" do
|
|
27
|
+
(1..2).should respond_to :bencode
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should encode to a bencoded list" do
|
|
31
|
+
(1..2).bencode.should == "li1ei2ee"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require "spec"
|
|
4
|
+
|
|
5
|
+
describe BEncode::Parser do
|
|
6
|
+
describe "#parse_object" do
|
|
7
|
+
# Most of this functionality is covered with other tests. So minimal stuff here.
|
|
8
|
+
it "should parse a bencoded string" do
|
|
9
|
+
scanner = StringScanner.new("6:string")
|
|
10
|
+
BEncode::Parser.parse_object(scanner).should == "string"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should parse a bencoded integer" do
|
|
14
|
+
scanner = StringScanner.new("i4e")
|
|
15
|
+
BEncode::Parser.parse_object(scanner).should == 4
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should parse a bencoded list" do
|
|
19
|
+
scanner = StringScanner.new("l6:stringeeeee")
|
|
20
|
+
BEncode::Parser.parse_object(scanner).should == ["string"]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "should parse a bencoded dictionary containing a key value pair" do
|
|
24
|
+
scanner = StringScanner.new("d6:stringi1ee")
|
|
25
|
+
BEncode::Parser.parse_object(scanner).should == {"string" => 1}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "should return nil when the type is not recognized" do
|
|
29
|
+
scanner = StringScanner.new("freak out!")
|
|
30
|
+
BEncode::Parser.parse_object(scanner).should == nil
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe "#parse_stirng" do
|
|
35
|
+
it "should parse a bencoded string" do
|
|
36
|
+
scanner = StringScanner.new("6:string")
|
|
37
|
+
BEncode::Parser.parse_string(scanner).should == "string"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "should parse a zero length bencoded string" do
|
|
41
|
+
scanner = StringScanner.new("0:")
|
|
42
|
+
BEncode::Parser.parse_string(scanner).should == ""
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "should raise an error if the length is invalid" do
|
|
46
|
+
scanner = StringScanner.new("fail:")
|
|
47
|
+
lambda {BEncode::Parser.parse_string(scanner)}.should raise_error BEncode::BEncodeError
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "should raise an error if length is too long" do
|
|
51
|
+
scanner = StringScanner.new("3:a")
|
|
52
|
+
lambda {BEncode::Parser.parse_string(scanner)}.should raise_error BEncode::BEncodeError
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "should raise an error if the colon is missing" do
|
|
56
|
+
scanner = StringScanner.new("3aaa")
|
|
57
|
+
lambda {BEncode::Parser.parse_string(scanner)}.should raise_error BEncode::BEncodeError
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
describe "#parse_integer" do
|
|
62
|
+
it "should parse a bencoded integer" do
|
|
63
|
+
scanner = StringScanner.new("i4e")
|
|
64
|
+
BEncode::Parser.parse_integer(scanner).should == 4
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "should raise an error if there is no starting i" do
|
|
68
|
+
scanner = StringScanner.new("4e")
|
|
69
|
+
lambda{BEncode::Parser.parse_integer(scanner)}.should raise_error BEncode::BEncodeError
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should raise an error if there is no integer" do
|
|
73
|
+
scanner = StringScanner.new("ie")
|
|
74
|
+
lambda{BEncode::Parser.parse_integer(scanner)}.should raise_error BEncode::BEncodeError
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "should raise an error if there is no closing e" do
|
|
78
|
+
scanner = StringScanner.new("i4")
|
|
79
|
+
lambda{BEncode::Parser.parse_integer(scanner)}.should raise_error BEncode::BEncodeError
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe "#parse_list" do
|
|
84
|
+
it "should parse an empty bencoded list" do
|
|
85
|
+
scanner = StringScanner.new("le")
|
|
86
|
+
BEncode::Parser.parse_list(scanner).should == []
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "should parse a bencoded list containing a string" do
|
|
90
|
+
scanner = StringScanner.new("l6:stringeeeee")
|
|
91
|
+
BEncode::Parser.parse_list(scanner).should == ["string"]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "should parse a bencoded list containing more than one string" do
|
|
95
|
+
scanner = StringScanner.new("l6:string6:stringe")
|
|
96
|
+
BEncode::Parser.parse_list(scanner).should == ["string", "string"]
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "should parse a bencoded list containing an integer" do
|
|
100
|
+
scanner = StringScanner.new("li1ee")
|
|
101
|
+
BEncode::Parser.parse_list(scanner).should == [1]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "should parse a bencoded list containing more than one integer" do
|
|
105
|
+
scanner = StringScanner.new("li1ei2ee")
|
|
106
|
+
BEncode::Parser.parse_list(scanner).should == [1, 2]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "should parse a bencoded list containing a list" do
|
|
110
|
+
scanner = StringScanner.new("llee")
|
|
111
|
+
BEncode::Parser.parse_list(scanner).should == [[]]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "should parse a bencoded list containing more than one list" do
|
|
115
|
+
scanner = StringScanner.new("llelee")
|
|
116
|
+
BEncode::Parser.parse_list(scanner).should == [[], []]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "should parse a bencoded list containing a dictionary" do
|
|
120
|
+
scanner = StringScanner.new("ldee")
|
|
121
|
+
BEncode::Parser.parse_list(scanner).should == [{}]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
it "should parse a bencoded list containing more than one dictionary" do
|
|
125
|
+
scanner = StringScanner.new("ldedee")
|
|
126
|
+
BEncode::Parser.parse_list(scanner).should == [{}, {}]
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "should raise an error if there is no starting l" do
|
|
130
|
+
scanner = StringScanner.new("e")
|
|
131
|
+
lambda{BEncode::Parser.parse_list(scanner)}.should raise_error BEncode::BEncodeError
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it "should raise an error if there is no closing e" do
|
|
135
|
+
scanner = StringScanner.new("l")
|
|
136
|
+
lambda{BEncode::Parser.parse_list(scanner)}.should raise_error BEncode::BEncodeError
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
describe "#parse_dictionary" do
|
|
141
|
+
it "should parse an empty bencoded dictionary" do
|
|
142
|
+
scanner = StringScanner.new("de")
|
|
143
|
+
BEncode::Parser.parse_dictionary(scanner).should == {}
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "should parse a bencoded dictionary containing a key value pair" do
|
|
147
|
+
scanner = StringScanner.new("d6:stringi1ee")
|
|
148
|
+
BEncode::Parser.parse_dictionary(scanner).should == {"string" => 1}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "should parse a bencoded dictionary containing more than one key value pair" do
|
|
152
|
+
scanner = StringScanner.new("d7:anotherle6:stringi1ee")
|
|
153
|
+
BEncode::Parser.parse_dictionary(scanner).should == {"string" => 1, "another" => []}
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
it "should raise an error if there is no starting d" do
|
|
157
|
+
scanner = StringScanner.new("e")
|
|
158
|
+
lambda{BEncode::Parser.parse_dictionary(scanner)}.should raise_error BEncode::BEncodeError
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
it "should raise an error if the key is not a string" do
|
|
162
|
+
scanner = StringScanner.new("di1ei1ee")
|
|
163
|
+
lambda{BEncode::Parser.parse_dictionary(scanner)}.should raise_error BEncode::BEncodeError
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "should raise an error if there is no closing e" do
|
|
167
|
+
scanner = StringScanner.new("d")
|
|
168
|
+
lambda{BEncode::Parser.parse_dictionary(scanner)}.should raise_error BEncode::BEncodeError
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "should raise an error if there is a key with no value" do
|
|
172
|
+
scanner = StringScanner.new("d1:ae")
|
|
173
|
+
lambda{BEncode::Parser.parse_dictionary(scanner)}.should raise_error BEncode::BEncodeError
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require "spec"
|
|
4
|
+
require "spec_helper"
|
|
5
|
+
|
|
6
|
+
describe String do
|
|
7
|
+
describe "#bencode" do
|
|
8
|
+
it "should encode a string" do
|
|
9
|
+
"string".bencode.should == "6:string"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "should encode a zero length string" do
|
|
13
|
+
"".bencode.should == "0:"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe Symbol do
|
|
19
|
+
describe "#bencode" do
|
|
20
|
+
it "should encode a symbol" do
|
|
21
|
+
:symbol.bencode.should == "6:symbol"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe URI::Generic do
|
|
27
|
+
describe "#bencode" do
|
|
28
|
+
it "should encode a http uri" do
|
|
29
|
+
uri = URI.parse("http://github.com/blatyo/bencode")
|
|
30
|
+
uri.bencode.should == "32:http://github.com/blatyo/bencode"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should encode a https uri" do
|
|
34
|
+
uri = URI.parse("https://github.com/blatyo/bencode")
|
|
35
|
+
uri.bencode.should == "33:https://github.com/blatyo/bencode"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "should encode a ftp uri" do
|
|
39
|
+
uri = URI.parse("ftp://github.com/blatyo/bencode")
|
|
40
|
+
uri.bencode.should == "31:ftp://github.com/blatyo/bencode"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should encode a ldap uri" do
|
|
44
|
+
uri = URI.parse("ldap://github.com/blatyo/bencode")
|
|
45
|
+
uri.bencode.should == "32:ldap://github.com/blatyo/bencode"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "should encode a mailto uri" do
|
|
49
|
+
uri = URI.parse("mailto:sudo@sudoers.su")
|
|
50
|
+
uri.bencode.should == "22:mailto:sudo@sudoers.su"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe BEncode::String do
|
|
56
|
+
describe "#register" do
|
|
57
|
+
context "once an object has been registered as a BEncode string" do
|
|
58
|
+
before :all do
|
|
59
|
+
BEncode::String.register Range
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context "an instance of that object" do
|
|
63
|
+
it "should respond to bencode" do
|
|
64
|
+
(1..2).should respond_to :bencode
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it "should encode to a bencoded string" do
|
|
68
|
+
(1..2).bencode.should == "4:1..2"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
require "spec"
|
|
4
|
+
require "spec_helper"
|
|
5
|
+
|
|
6
|
+
describe BEncode do
|
|
7
|
+
describe "#decode" do
|
|
8
|
+
# Most of this is covered in other tests. Only difference is this accepts string instead of scanner.
|
|
9
|
+
it "should parse a bencoded string" do
|
|
10
|
+
BEncode.decode("6:string").should == "string"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should parse a bencoded integer" do
|
|
14
|
+
BEncode.decode("i4e").should == 4
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should parse a bencoded list" do
|
|
18
|
+
BEncode.decode("l6:stringeeeee").should == ["string"]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should parse a bencoded dictionary containing a key value pair" do
|
|
22
|
+
BEncode.decode("d6:stringi1ee").should == {"string" => 1}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should raise an error when the type is not recognized" do
|
|
26
|
+
lambda{BEncode.decode("freak out!")}.should raise_error BEncode::BEncodeError
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "#decode_file" do
|
|
31
|
+
it "should parse a bencoded file" do
|
|
32
|
+
dirname = File.dirname(__FILE__)
|
|
33
|
+
BEncode.decode_file("#{dirname}/samples/mini.bencode").should == {"ba" => 3}
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe "#encode" do
|
|
38
|
+
# Covered in other tests so only simple stuff here.
|
|
39
|
+
it "should bencode an object" do
|
|
40
|
+
BEncode.encode("string").should == "6:string"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe "#encode_file" do
|
|
45
|
+
context "when an object gets bencoded and written to a file" do
|
|
46
|
+
before :each do
|
|
47
|
+
@file = File.join(File.dirname(__FILE__), '..', 'tmp', 'test.bencode')
|
|
48
|
+
@object = "string"
|
|
49
|
+
BEncode.encode_file(@file, @object)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should actually write a file" do
|
|
53
|
+
File.exists?(@file).should be_true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should properly encode the file" do
|
|
57
|
+
BEncode.decode_file(@file).should == @object
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
after :each do
|
|
61
|
+
File.delete(@file)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context "when parsing and then encoding" do
|
|
67
|
+
it "should be equal to the pre-parsed and encoded bencoded string" do
|
|
68
|
+
file = File.dirname(__FILE__) + "/samples/bencode.rb.torrent"
|
|
69
|
+
BEncode.decode_file(file).bencode.should == File.open(file, "rb") {|f| f.read}
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
d8:announce42:http://tracker.openbittorrent.com/announce13:announce-listll42:http://tracker.openbittorrent.com/announce44:udp://tracker.openbittorrent.com:80/announceee7:comment28:Best bencoding library ever!10:created by13:uTorrent/185013:creation datei1264866817e8:encoding5:UTF-84:infod6:lengthi526e4:name10:bencode.rb12:piece lengthi65536e6:pieces20:�WiP�2�n�����U��W&��ee
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
d2:bai3ee
|
data/spec/spec.opts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED