protopuffs 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.dirname(__FILE__) + '/test_helper'
4
+
5
+ class MessageFieldTest < Test::Unit::TestCase
6
+
7
+ context "creating a MessageField" do
8
+ should "not allow you to instantiate MessageField directly" do
9
+ assert_raises RuntimeError do
10
+ Protopuffs::MessageField.new "_", "_", "_", "_"
11
+ end
12
+ end
13
+
14
+ should "complain if an invalid modifier is specified" do
15
+ ["optional", "required", "repeated"].each do |modifier|
16
+ assert_nothing_raised {Protopuffs::Int32.new(modifier, 'identitifer', 1)}
17
+ end
18
+ assert_raises(ArgumentError) {Protopuffs::Int32.new("invalid_modifier", "identifier", 1)}
19
+ end
20
+
21
+ should "instantiate the right class, given a type string" do
22
+ [[ "int32", Protopuffs::Int32],
23
+ [ "int64", Protopuffs::Int64],
24
+ [ "uint32", Protopuffs::UInt32],
25
+ [ "uint64", Protopuffs::UInt64],
26
+ [ "bool", Protopuffs::Bool],
27
+ [ "double", Protopuffs::Double],
28
+ [ "fixed64", Protopuffs::Fixed64],
29
+ [ "string", Protopuffs::String],
30
+ [ "bytes", Protopuffs::Bytes],
31
+ [ "float", Protopuffs::Float],
32
+ [ "fixed32", Protopuffs::Fixed32],
33
+ [ "sfixed32", Protopuffs::SFixed32],
34
+ [ "embedded", Protopuffs::Embedded],
35
+ ].each do |type, klass|
36
+ assert_kind_of klass, Protopuffs::MessageField.factory(type, "optional", "a_string", 1, 2)
37
+ end
38
+ end
39
+
40
+ should "set a string's default to '' when a default isn't specified" do
41
+ field = Protopuffs::String.new("optional", "name", 1)
42
+ assert_equal "", field.default
43
+ end
44
+
45
+ should "set a string's default to '' when a default is specified as nil" do
46
+ field = Protopuffs::String.new("optional", "name", 1, nil)
47
+ assert_equal "", field.default
48
+ end
49
+
50
+ should "set a numeric's default to 0 when a default isn't specified or is specified as nil" do
51
+ numeric_types = [Protopuffs::Double, Protopuffs::Float,
52
+ Protopuffs::Int32, Protopuffs::Int64,
53
+ Protopuffs::UInt32, Protopuffs::UInt64,
54
+ Protopuffs::Fixed32, Protopuffs::Fixed64,
55
+ Protopuffs::SFixed32]
56
+ numeric_types.each do |type|
57
+ assert_equal 0, type.new("optional", "number", 1).default
58
+ assert_equal 0, type.new("optional", "number", 1, nil).default
59
+ end
60
+ end
61
+
62
+ should "set a bool's default to false when a default isn't specified" do
63
+ field = Protopuffs::Bool.new("optional", "opt_in", 1)
64
+ assert_same false, field.default
65
+ end
66
+
67
+ should "set a bool's default to false when a default is specified as nil" do
68
+ field = Protopuffs::Bool.new("optional", "opt_in", 1, nil)
69
+ assert_same false, field.default
70
+ end
71
+
72
+ should "set the default to 'Matz' when that default is specified" do
73
+ field = Protopuffs::String.new("optional", "name", 1, "Matz")
74
+ assert_equal "Matz", field.default
75
+ end
76
+ end
77
+
78
+ end
@@ -0,0 +1,208 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.dirname(__FILE__) + '/test_helper'
4
+
5
+ class ParseTreeTest < Test::Unit::TestCase
6
+
7
+ context "a protocol buffer descriptor" do
8
+ setup do
9
+ @parser = Protopuffs::Parser::ProtocolBufferDescriptor.new
10
+ end
11
+
12
+ context "with an empty Person message" do
13
+ setup do
14
+ @proto = @parser.parse("message Person {}")
15
+ end
16
+
17
+ should "have one message" do
18
+ assert_equal 1, @proto.messages.size
19
+ end
20
+
21
+ should "have a message named Person" do
22
+ assert_equal "Person", @proto.messages.first.name
23
+ end
24
+
25
+ should "have an empty body" do
26
+ assert @proto.messages.first.body.empty?
27
+ end
28
+ end
29
+
30
+ context "with two empty messages Apple and Orange" do
31
+ setup do
32
+ @descriptor = <<-proto
33
+ message Apple {}
34
+ message Orange {}
35
+ proto
36
+ end
37
+
38
+ should "have two messages" do
39
+ proto = @parser.parse(@descriptor)
40
+ assert_equal 2, proto.messages.size
41
+ end
42
+
43
+ should "have messages named Apple and Orange" do
44
+ proto = @parser.parse(@descriptor)
45
+ assert_equal %w(Apple Orange), proto.messages.map { |m| m.name }.sort
46
+ end
47
+
48
+ should "have messages with empty bodes" do
49
+ proto = @parser.parse(@descriptor)
50
+ assert proto.messages.all? { |m| m.body.empty? }
51
+ end
52
+ end
53
+
54
+ context "a proto with a Person message including a name field" do
55
+ setup do
56
+ @descriptor = <<-proto
57
+ message Person {
58
+ required string name = 1;
59
+ }
60
+ proto
61
+ end
62
+
63
+ should "have one message named person" do
64
+ proto = @parser.parse(@descriptor)
65
+ assert_equal 1, proto.messages.size
66
+ assert_equal "Person", proto.messages.first.name
67
+ end
68
+
69
+ should "have one required string field called name with tag 1" do
70
+ proto = @parser.parse(@descriptor)
71
+ fields = proto.messages.first.body.fields
72
+ assert_equal 1, fields.size
73
+ assert_equal "required", fields.first.modifier.text_value
74
+ assert_equal "string", fields.first.type.text_value
75
+ assert_equal "name", fields.first.identifier.text_value
76
+ assert_equal "1", fields.first.integer.text_value
77
+ end
78
+ end
79
+
80
+ context "with a Person message including three fields" do
81
+ setup do
82
+ @descriptor = <<-proto
83
+ message Person {
84
+ required string name = 1;
85
+ required int32 id = 2;
86
+ optional string email = 3;
87
+ }
88
+ proto
89
+ end
90
+
91
+ should "have one message named person" do
92
+ proto = @parser.parse(@descriptor)
93
+ assert_equal 1, proto.messages.size
94
+ assert_equal "Person", proto.messages.first.name
95
+ end
96
+
97
+ should "have three fields with correct components" do
98
+ proto = @parser.parse(@descriptor)
99
+ fields = proto.messages.first.body.fields
100
+ assert_equal 3, fields.size
101
+ actual = fields.map { |f| [f.modifier, f.type, f.identifier, f.integer].map { |el| el.text_value } }
102
+ expected = [ %w(required string name 1),
103
+ %w(required int32 id 2),
104
+ %w(optional string email 3) ]
105
+ assert_equal expected, actual
106
+ end
107
+ end
108
+
109
+ context "with a Person message including two fields with defaults and one without" do
110
+ setup do
111
+ @proto = @parser.parse(<<-proto)
112
+ message Person {
113
+ required string name = 1;
114
+ optional string language = 2 [default = "en"];
115
+ optional int32 account_code = 3 [default = 0];
116
+ }
117
+ proto
118
+ end
119
+
120
+ should "have one message named person" do
121
+ assert_equal 1, @proto.messages.size
122
+ assert_equal "Person", @proto.messages.first.name
123
+ end
124
+
125
+ should "have three fields with correct components" do
126
+ fields = @proto.messages.first.body.fields
127
+ assert_equal 3, fields.size
128
+ actual = fields.map { |f| [f.modifier, f.type, f.identifier, f.integer, f.default] }
129
+ actual.map! { |f| f.map! { |el| el.respond_to?(:text_value) ? el.text_value : el } }
130
+ expected = [ ["required", "string", "name", "1", nil],
131
+ ["optional", "string", "language", "2", "en"],
132
+ ["optional", "int32", "account_code", "3", 0] ]
133
+ assert_equal expected, actual
134
+ end
135
+ end
136
+
137
+ context "with a message including lots of comments" do
138
+ setup do
139
+ @proto = @parser.parse(<<-proto)
140
+ // test
141
+ //test
142
+ message Person { //test // test
143
+ // test
144
+ required string name = 1; // test
145
+ // optional string language = 2 [default = "en"];
146
+ optional int32 account_code = 3 [default = 0]; // test
147
+ // test
148
+ } // test
149
+ // test
150
+ //test
151
+ proto
152
+ end
153
+
154
+ should "parse" do
155
+ assert_not_nil @proto
156
+ end
157
+
158
+ should "have uncommented fields with correct components" do
159
+ fields = @proto.messages.first.body.fields
160
+ assert_equal 2, fields.size
161
+ actual = fields.map { |f| [f.modifier, f.type, f.identifier, f.integer, f.default] }
162
+ actual.map! { |f| f.map! { |el| el.respond_to?(:text_value) ? el.text_value : el } }
163
+ expected = [ ["required", "string", "name", "1", nil],
164
+ ["optional", "int32", "account_code", "3", 0] ]
165
+ assert_equal expected, actual
166
+ end
167
+ end
168
+
169
+
170
+ context "with a message including a user-typed field" do
171
+ setup do
172
+ @proto = @parser.parse(<<-proto)
173
+ message Person {
174
+ required string name = 1;
175
+ repeated Address addresses = 2;
176
+ }
177
+ proto
178
+ end
179
+
180
+ should "parse" do
181
+ assert_not_nil @proto
182
+ end
183
+
184
+ should "have two fields with correct components" do
185
+ fields = @proto.messages.first.body.fields
186
+ assert_equal 2, fields.size
187
+ actual = fields.map { |f| [f.modifier, f.type, f.identifier, f.integer] }
188
+ actual.map! { |f| f.map! { |el| el.respond_to?(:text_value) ? el.text_value : el } }
189
+ expected = [ ["required", "string", "name", "1"],
190
+ ["repeated", "Address", "addresses", "2"] ]
191
+ assert_equal expected, actual
192
+ end
193
+ end
194
+
195
+
196
+ should "raise a ParseError when parsing a message with a syntax error" do
197
+ assert_raises Protopuffs::ParseError do
198
+ @parser.parse(<<-proto)
199
+ message Person {
200
+ required name = 1
201
+ }
202
+ proto
203
+ end
204
+ end
205
+
206
+ end
207
+
208
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.dirname(__FILE__) + '/test_helper'
4
+
5
+ class ProtopuffsTest < Test::Unit::TestCase
6
+
7
+ context ".proto_load_path accessors" do
8
+ setup { Protopuffs.proto_load_path = [] }
9
+
10
+ should "have an accessor for an array of load paths for .proto files" do
11
+ Protopuffs.proto_load_path << "proto_files" << "other_proto_files"
12
+ assert_equal ["proto_files", "other_proto_files"], Protopuffs.proto_load_path
13
+ end
14
+
15
+ should "have a mutator for directly assigning the load paths" do
16
+ Protopuffs.proto_load_path = ["my_proto_files"]
17
+ assert_equal ["my_proto_files"], Protopuffs.proto_load_path
18
+ end
19
+ end
20
+
21
+ should "have a ParseError class" do
22
+ Protopuffs::ParseError
23
+ end
24
+
25
+
26
+ context ".load_message_classes when a descriptor is in the load path with a Person message" do
27
+ setup { Protopuffs.proto_load_path = ["#{File.dirname(__FILE__)}/fixtures/proto"] }
28
+
29
+ should "create a Person message class with correct accessors" do
30
+ Protopuffs.load_message_classes
31
+ p = Protopuffs::Message::Person.new
32
+ p.name = "Chris"
33
+ p.id = 42
34
+ p.email = "chris@kampers.net"
35
+ assert_equal ["Chris", 42, "chris@kampers.net"], [p.name, p.id, p.email]
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,90 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rubygems'
4
+ require 'test/unit'
5
+
6
+ gem "polyglot", "=0.2.9"
7
+ gem "treetop", "=1.4.2"
8
+
9
+ gem "thoughtbot-shoulda", "=2.10.2"
10
+ require 'shoulda'
11
+
12
+ gem "mocha", "=0.9.8"
13
+ require 'mocha'
14
+
15
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
16
+ require 'protopuffs'
17
+
18
+ class Test::Unit::TestCase
19
+
20
+ # helper for debugging
21
+ def print_bytes(string)
22
+ puts
23
+ string.each_byte do |byte|
24
+ printf("%1$08b (%1$02X)\n", byte)
25
+ end
26
+ end
27
+
28
+ # helper for generating test names
29
+ def self.inspect_bytes(bytes_array)
30
+ "[#{bytes_array.map { |b| sprintf("%02X", b) }.join(' ')}]"
31
+ end
32
+
33
+
34
+ def self.should_encode_wire_format_from_fields(expected_bytes, actual_fields)
35
+ should "encode the fields #{actual_fields.inspect} to the byte string #{inspect_bytes(expected_bytes)}" do
36
+ actual_fields.each_pair do |name, value|
37
+ value = value.call if value.respond_to?(:call)
38
+ @message.send("#{name}=", value)
39
+ end
40
+ actual_bytes = @message.to_wire_format.unpack('C*')
41
+ assert_equal expected_bytes, actual_bytes
42
+ end
43
+ end
44
+
45
+ def self.should_decode_wire_format_to_fields(actual_bytes, expected_fields)
46
+ should "decode the byte string #{inspect_bytes(actual_bytes)} to the fields #{expected_fields.inspect}" do
47
+ buffer = StringIO.new(actual_bytes.pack('C*'))
48
+ buffer.set_encoding("BINARY") if buffer.respond_to?(:set_encoding)
49
+ @message.from_wire_format(buffer)
50
+ actual_fields = @message.class.fields.inject({}) { |hash, field|
51
+ hash[field.identifier.to_sym] = @message.send(field.identifier)
52
+ hash
53
+ }
54
+
55
+ expected_fields.each_pair do |key, expected_value|
56
+ expected_value = expected_value.call if expected_value.respond_to?(:call)
57
+ if expected_value.is_a?(Float)
58
+ assert_in_delta(expected_value, actual_fields[key], Float::EPSILON)
59
+ else
60
+ assert_equal expected_value, actual_fields[key]
61
+ end
62
+ end
63
+
64
+ assert_equal expected_fields.size, actual_fields.size
65
+ end
66
+ end
67
+
68
+ def self.should_encode_and_decode_wire_format_and_fields(bytes, fields)
69
+ self.should_encode_wire_format_from_fields(bytes, fields)
70
+ self.should_decode_wire_format_to_fields(bytes, fields)
71
+ end
72
+
73
+ def self.should_losslessly_encode_and_decode_a_random_sample(fields)
74
+ raise ArgumentError if fields.size > 1
75
+ name, range = fields.shift
76
+ should "get the same value back after encoding and decoding a random sample of the values #{range.inspect} for field #{name.inspect}" do
77
+ size = range.last - range.first
78
+ size -= 1 if range.exclude_end?
79
+ values_to_test = [range.first, range.first + size]
80
+ 250.times { values_to_test << range.first + rand(size) }
81
+ values_to_test.each do |value|
82
+ @message.send("#{name}=", value)
83
+ @message.from_wire_format(@message.to_wire_format)
84
+ assert_equal value, @message.send(name)
85
+ end
86
+ end
87
+ end
88
+
89
+ end
90
+
@@ -0,0 +1,23 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.dirname(__FILE__) + '/test_helper'
4
+
5
+ class TextFormatTest < Test::Unit::TestCase
6
+
7
+ context "a message called Test1 with one int32 field" do
8
+ # from http://code.google.com/apis/protocolbuffers/docs/overview.html#whynotxml
9
+ setup do
10
+ fields = [Protopuffs::String.new("required", "name", 1),
11
+ Protopuffs::String.new("required", "email", 2)]
12
+ Protopuffs::Message::Base.define_message_class("Person", fields)
13
+ @message = Protopuffs::Message::Person.new
14
+ @message.name = "John Doe"
15
+ @message.email = "jdoe@example.com"
16
+ end
17
+
18
+ should "return the correct text format from #inspect" do
19
+ assert_equal %Q(person {\n name: "John Doe"\n email: "jdoe@example.com"\n}), @message.inspect
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,293 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.dirname(__FILE__) + '/test_helper'
4
+
5
+ class WireFormatTest < Test::Unit::TestCase
6
+
7
+ context "a message with one int32 field tagged #1" do
8
+ # from http://code.google.com/apis/protocolbuffers/docs/encoding.html#simple
9
+ setup do
10
+ fields = [Protopuffs::Int32.new("required", "a", 1)]
11
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
12
+ @message = Protopuffs::Message::Test1.new
13
+ end
14
+
15
+ should_encode_wire_format_from_fields [0x08, 0x96, 0x01], :a => 150
16
+ should_decode_wire_format_to_fields [0x08, 0x96, 0x01], :a => 150
17
+
18
+ # should ignore unknown fields: this message also has an int32 tagged #2 with value 157,372
19
+ should_decode_wire_format_to_fields [0x08, 0x96, 0x01, 0x10, 0xBC, 0xCD, 0x09], :a => 150
20
+
21
+ should "return itself from #from_wire_format" do
22
+ wire_message = StringIO.new([0x08, 0x96, 0x01].pack('C*'))
23
+ assert_same @message, @message.from_wire_format(wire_message)
24
+ end
25
+
26
+ should "accept a string as an argument to #from_wire_format" do
27
+ wire_message = [0x08, 0x96, 0x01].pack('C*')
28
+ @message.from_wire_format(wire_message)
29
+ assert_equal 150, @message.a
30
+ end
31
+ end
32
+
33
+
34
+ context "a message with two int32 fields tagged #1 and #2" do
35
+ setup do
36
+ fields = [Protopuffs::Int32.new("required", "a", 1),
37
+ Protopuffs::Int32.new("required", "b", 2)]
38
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
39
+ @message = Protopuffs::Message::Test1.new
40
+ end
41
+
42
+ should_encode_wire_format_from_fields [0x08, 0x96, 0x01, 0x10, 0xBC, 0xCD, 0x09],
43
+ :a => 150, :b => 157_372
44
+ should_decode_wire_format_to_fields [0x08, 0x96, 0x01, 0x10, 0xBC, 0xCD, 0x09],
45
+ :a => 150, :b => 157_372
46
+ end
47
+
48
+
49
+ context "a message with one int64 field tagged #1" do
50
+ setup do
51
+ fields = [Protopuffs::Int64.new("required", "a", 1)]
52
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
53
+ @message = Protopuffs::Message::Test1.new
54
+ end
55
+
56
+ should_encode_wire_format_from_fields [0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02],
57
+ :a => 2**50
58
+ should_decode_wire_format_to_fields [0x08, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02],
59
+ :a => 2**50
60
+ end
61
+
62
+
63
+ context "a message with one uint32 field tagged #1 and one uint64 field tagged #2" do
64
+ setup do
65
+ fields = [Protopuffs::UInt32.new("required", "a", 1),
66
+ Protopuffs::UInt64.new("required", "b", 2)]
67
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
68
+ @message = Protopuffs::Message::Test1.new
69
+ end
70
+
71
+ should_encode_wire_format_from_fields [0x08, 0x90, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x20],
72
+ :a => 912, :b => 2**54
73
+ should_decode_wire_format_to_fields [0x08, 0x90, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x20],
74
+ :a => 912, :b => 2**54
75
+ end
76
+
77
+
78
+ context "a message with two bool fields tagged #1 and #2" do
79
+ setup do
80
+ fields = [Protopuffs::Bool.new("required", "a", 1),
81
+ Protopuffs::Bool.new("required", "b", 2)]
82
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
83
+ @message = Protopuffs::Message::Test1.new
84
+ end
85
+
86
+ should_encode_wire_format_from_fields [0x08, 0x00, 0x10, 0x01],
87
+ :a => false, :b => true
88
+ should_decode_wire_format_to_fields [0x08, 0x00, 0x10, 0x01],
89
+ :a => false, :b => true
90
+ end
91
+
92
+
93
+ context "a message with one string field tagged #2" do
94
+ setup do
95
+ # from http://code.google.com/apis/protocolbuffers/docs/encoding.html#types
96
+ fields = [Protopuffs::String.new("required", "b", 2)]
97
+ Protopuffs::Message::Base.define_message_class("Test2", fields)
98
+ @message = Protopuffs::Message::Test2.new
99
+ end
100
+
101
+ should_encode_wire_format_from_fields [0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6E, 0x67],
102
+ :b => "testing"
103
+ should_decode_wire_format_to_fields [0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6E, 0x67],
104
+ :b => "testing"
105
+
106
+ should_encode_wire_format_from_fields [0x12, 0x01, 0x32], :b => 2
107
+ should_decode_wire_format_to_fields [0x12, 0x01, 0x32], :b => "2"
108
+
109
+ should_encode_wire_format_from_fields [0x12, 0x0C, 0xE3, 0x81, 0x93, 0xE3, 0x81, 0xAB, 0xE3, 0x81, 0xA1, 0xE3, 0x82, 0x8F],
110
+ :b => "こにちわ"
111
+ should_decode_wire_format_to_fields [0x12, 0x0C, 0xE3, 0x81, 0x93, 0xE3, 0x81, 0xAB, 0xE3, 0x81, 0xA1, 0xE3, 0x82, 0x8F],
112
+ :b => "こにちわ"
113
+
114
+ should_encode_wire_format_from_fields [0x12, 0x05, 0xD2, 0x90, 0x41, 0xC3, 0x9C],
115
+ :b => "ҐAÜ"
116
+ should_decode_wire_format_to_fields [0x12, 0x05, 0xD2, 0x90, 0x41, 0xC3, 0x9C],
117
+ :b => "ҐAÜ"
118
+ end
119
+
120
+
121
+ context "a message with a bytes field tagged #1" do
122
+ setup do
123
+ fields = [Protopuffs::Bytes.new("required", "a", 1)]
124
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
125
+ @message = Protopuffs::Message::Test1.new
126
+ end
127
+
128
+ should_encode_wire_format_from_fields [0x0A, 0x04, 0xDE, 0xCA, 0xFB, 0xAD],
129
+ :a => [0xDE, 0xCA, 0xFB, 0xAD].pack('C*')
130
+ should_decode_wire_format_to_fields [0x0A, 0x04, 0xDE, 0xCA, 0xFB, 0xAD],
131
+ :a => [0xDE, 0xCA, 0xFB, 0xAD].pack('C*')
132
+ end
133
+
134
+
135
+ context "a message with one float field tagged #1" do
136
+ setup do
137
+ fields = [Protopuffs::Float.new("required", "a", 1)]
138
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
139
+ @message = Protopuffs::Message::Test1.new
140
+ end
141
+
142
+ # 1.61803 gives you repeating binary digits when encoded, so the number
143
+ # you get from decoding is different (within Float::EPSILON)
144
+ should_encode_wire_format_from_fields [0x0D, 0x9B, 0x1B, 0xCF, 0x3F],
145
+ :a => 1.61803
146
+ should_decode_wire_format_to_fields [0x0D, 0x9B, 0x1B, 0xCF, 0x3F],
147
+ :a => 1.6180299520492554
148
+ end
149
+
150
+
151
+ context "a message with one double field tagged #1" do
152
+ setup do
153
+ fields = [Protopuffs::Double.new("required", "a", 1)]
154
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
155
+ @message = Protopuffs::Message::Test1.new
156
+ end
157
+
158
+ # 64-bit doubles have enough precision to encode/decode 1.61803,
159
+ # unlike the 32-bit floats above
160
+ should_encode_wire_format_from_fields [0x09, 0x6C, 0x26, 0xDF, 0x6C, 0x73, 0xE3, 0xF9, 0x3F],
161
+ :a => 1.61803
162
+ should_decode_wire_format_to_fields [0x09, 0x6C, 0x26, 0xDF, 0x6C, 0x73, 0xE3, 0xF9, 0x3F],
163
+ :a => 1.61803
164
+ end
165
+
166
+
167
+ context "a message with one fixed64 field tagged #1" do
168
+ setup do
169
+ fields = [Protopuffs::Fixed64.new("required", "a", 1)]
170
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
171
+ @message = Protopuffs::Message::Test1.new
172
+ end
173
+
174
+ should_encode_wire_format_from_fields [0x09, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F],
175
+ :a => 2**62 - 15
176
+ should_decode_wire_format_to_fields [0x09, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F],
177
+ :a => 2**62 - 15
178
+
179
+ end
180
+
181
+
182
+ context "a message with one fixed32 field tagged #1" do
183
+ setup do
184
+ fields = [Protopuffs::Fixed32.new("required", "a", 1)]
185
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
186
+ @message = Protopuffs::Message::Test1.new
187
+ end
188
+
189
+ should_encode_wire_format_from_fields [0x0D, 0xFE, 0xFF, 0xFF, 0xFF],
190
+ :a => 2**32 - 2
191
+ should_decode_wire_format_to_fields [0x0D, 0xFE, 0xFF, 0xFF, 0xFF],
192
+ :a => 2**32 - 2
193
+ end
194
+
195
+
196
+ context "a message with one sfixed32 field tagged #1" do
197
+ setup do
198
+ fields = [Protopuffs::SFixed32.new("required", "a", 1)]
199
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
200
+ @message = Protopuffs::Message::Test1.new
201
+ end
202
+
203
+ should_encode_and_decode_wire_format_and_fields [0x0D, 0x05, 0x00, 0x00, 0x80],
204
+ :a => -2**31 + 5
205
+ should_encode_and_decode_wire_format_and_fields [0x0D, 0x05, 0x80, 0x00, 0x00],
206
+ :a => 2**15 + 5
207
+ should_losslessly_encode_and_decode_a_random_sample :a => -2**31...2**31
208
+ end
209
+
210
+
211
+ context "a message with one repeating int32 field tagged #1" do
212
+ setup do
213
+ fields = [Protopuffs::Int32.new("repeated", "a", 1)]
214
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
215
+ @message = Protopuffs::Message::Test1.new
216
+ end
217
+
218
+ should_encode_wire_format_from_fields [0x08, 0x96, 0x01, 0x08, 0xBC, 0xCD, 0x09, 0x08, 0x3D],
219
+ :a => [150, 157_372, 61]
220
+ should_decode_wire_format_to_fields [0x08, 0x96, 0x01, 0x08, 0xBC, 0xCD, 0x09, 0x08, 0x3D],
221
+ :a => [150, 157_372, 61]
222
+ end
223
+
224
+
225
+ context "a message with one embedded-message field Test1 tagged #3 (where Test1 has an int32 field tagged #1)" do
226
+ # from http://code.google.com/apis/protocolbuffers/docs/encoding.html#embedded
227
+ setup do
228
+ test1_fields = [Protopuffs::Int32.new("required", "a", 1)]
229
+ Protopuffs::Message::Base.define_message_class("Test1", test1_fields)
230
+
231
+ test3_fields = [Protopuffs::Embedded.new("Test1", "required", "c", 3)]
232
+ Protopuffs::Message::Base.define_message_class("Test3", test3_fields)
233
+ @message = Protopuffs::Message::Test3.new
234
+ end
235
+
236
+ should_encode_wire_format_from_fields [0x1A, 0x03, 0x08, 0x96, 0x01],
237
+ :c => lambda { msg = Protopuffs::Message::Test1.new; msg.a = 150; msg }
238
+ should_decode_wire_format_to_fields [0x1A, 0x03, 0x08, 0x96, 0x01],
239
+ :c => lambda { msg = Protopuffs::Message::Test1.new; msg.a = 150; msg }
240
+ end
241
+
242
+
243
+ context "a message with two int32 fields tagged #1 (optional, default=150) and #2 (required)" do
244
+ setup do
245
+ fields = [Protopuffs::Int32.new("optional", "a", 1, 150),
246
+ Protopuffs::Int32.new("required", "b", 2)]
247
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
248
+ @message = Protopuffs::Message::Test1.new
249
+ end
250
+
251
+ should_encode_wire_format_from_fields [0x10, 0xBC, 0xCD, 0x09],
252
+ :b => 157_372
253
+ should_decode_wire_format_to_fields [0x10, 0xBC, 0xCD, 0x09],
254
+ :a => 150, :b => 157_372
255
+ end
256
+
257
+
258
+ context "a message with two int32 fields tagged #1 (optional, no default) and #2 (required)" do
259
+ setup do
260
+ fields = [Protopuffs::Int32.new("optional", "a", 1),
261
+ Protopuffs::Int32.new("required", "b", 2)]
262
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
263
+ @message = Protopuffs::Message::Test1.new
264
+ end
265
+
266
+ should_encode_wire_format_from_fields [0x10, 0xBC, 0xCD, 0x09],
267
+ :b => 157_372
268
+ should_decode_wire_format_to_fields [0x10, 0xBC, 0xCD, 0x09],
269
+ :a => 0, :b => 157_372
270
+ end
271
+
272
+
273
+ context "a message with two int32 fields tagged #1 and #2, defined with #2 first" do
274
+ setup do
275
+ fields = [Protopuffs::Int32.new("required", "b", 2),
276
+ Protopuffs::Int32.new("required", "a", 1)]
277
+ Protopuffs::Message::Base.define_message_class("Test1", fields)
278
+ @message = Protopuffs::Message::Test1.new
279
+ end
280
+
281
+ # should always encode with fields ordered by tag number, to take
282
+ # advantage of decoders that optimize for this case
283
+ should_encode_wire_format_from_fields [0x08, 0x14, 0x10, 0xBC, 0xCD, 0x09],
284
+ :a => 20, :b => 157_372
285
+
286
+ # the decoder should still support any field order, though
287
+ should_decode_wire_format_to_fields [0x08, 0x14, 0x10, 0xBC, 0xCD, 0x09],
288
+ :a => 20, :b => 157_372
289
+ should_decode_wire_format_to_fields [0x10, 0xBC, 0xCD, 0x09, 0x08, 0x14],
290
+ :a => 20, :b => 157_372
291
+ end
292
+
293
+ end