simrpc 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,26 @@
1
+ # simrpc common module spec
2
+ #
3
+ # Copyright (c) 2010 Mohammed Morsi <movitto@yahoo.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person
6
+ # obtaining a copy of this software and associated documentation
7
+ # files (the "Software"), to deal in the Software without
8
+ # restriction, including without limitation the rights to use,
9
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the
11
+ # Software is furnished to do so, subject to the following
12
+ # conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ # OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ require File.dirname(__FILE__) + '/spec_helper'
@@ -0,0 +1,93 @@
1
+ # simrpc message spec
2
+ #
3
+ # Copyright (c) 2010 Mohammed Morsi <movitto@yahoo.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person
6
+ # obtaining a copy of this software and associated documentation
7
+ # files (the "Software"), to deal in the Software without
8
+ # restriction, including without limitation the rights to use,
9
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the
11
+ # Software is furnished to do so, subject to the following
12
+ # conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ # OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ require File.dirname(__FILE__) + '/spec_helper'
27
+
28
+ describe "Simrpc::Message" do
29
+
30
+ ################## test message module
31
+
32
+ it "should format and parse a data field" do
33
+ formatted = Message::Formatter::format_with_size("foobar")
34
+ formatted.should == "00000006foobar"
35
+ assert_equal ["foobar", ""], Message::Formatter::parse_from_formatted(formatted)
36
+ end
37
+
38
+ it "should convert a field to and from a string" do
39
+ field = Message::Field.new(:name => "foo", :value => "bar")
40
+ field_s = field.to_s
41
+ field_s.should == "00000003foo00000003bar"
42
+
43
+ field_o = Message::Field.from_s(field_s)
44
+ field_o.name.should == "foo"
45
+ field_o.value.should == "bar"
46
+ end
47
+
48
+ it "should convert a message header to and from a string" do
49
+ header = Message::Header.new(:target => "footarget")
50
+ header_s = header.to_s
51
+ header_s.should == "0000000000000009footarget"
52
+
53
+ header_o = Message::Header.from_s(header_s)
54
+ header.type.should == ""
55
+ header.target.should == "footarget"
56
+ end
57
+
58
+ it "should convert a message body to and from a string" do
59
+ body = Message::Body.new
60
+ body.fields.push Message::Field.new(:name => "foo", :value => "bar")
61
+ body.fields.push Message::Field.new(:name => "money", :value => "lotsof")
62
+ body_s = body.to_s
63
+ body_s.should == "0000002200000003foo00000003bar0000002700000005money00000006lotsof"
64
+
65
+ body_o = Message::Body.from_s(body_s)
66
+ body_o.fields.size.should == 2
67
+ body_o.fields[0].name.should == "foo"
68
+ body_o.fields[0].value.should == "bar"
69
+ body_o.fields[1].name.should == "money"
70
+ body_o.fields[1].value.should == "lotsof"
71
+ end
72
+
73
+ it "should convert a message to and from a string" do
74
+ msg = Message::Message.new
75
+ msg.header.type = 'request'
76
+ msg.header.target = 'method'
77
+ msg.body.fields.push Message::Field.new(:name => "foo", :value => "bar")
78
+ msg.body.fields.push Message::Field.new(:name => "money", :value => "lotsof")
79
+ msg_s = msg.to_s
80
+
81
+ msg_s.should == "0000002900000007request00000006method000000650000002200000003foo00000003bar0000002700000005money00000006lotsof"
82
+
83
+ msg_o = Message::Message.from_s(msg_s)
84
+ msg_o.header.type.should == 'request'
85
+ msg_o.header.target.should == 'method'
86
+ msg_o.body.fields.size.should == 2
87
+ msg_o.body.fields[0].name.should == 'foo'
88
+ msg_o.body.fields[0].value.should == 'bar'
89
+ msg_o.body.fields[1].name.should == 'money'
90
+ msg_o.body.fields[1].value.should == 'lotsof'
91
+ end
92
+
93
+ end
data/spec/node_spec.rb ADDED
@@ -0,0 +1,99 @@
1
+ # simrpc node adapter spec
2
+ #
3
+ # Copyright (c) 2010 Mohammed Morsi <movitto@yahoo.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person
6
+ # obtaining a copy of this software and associated documentation
7
+ # files (the "Software"), to deal in the Software without
8
+ # restriction, including without limitation the rights to use,
9
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the
11
+ # Software is furnished to do so, subject to the following
12
+ # conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ # OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ require File.dirname(__FILE__) + '/spec_helper'
27
+
28
+ describe "Simrpc::Node" do
29
+
30
+ it "should generate and handle simrpc message" do
31
+ schema_def = Schema::Parser.parse(:schema => TEST_SCHEMA)
32
+ mmc = MethodMessageController.new(schema_def)
33
+ msg = mmc.generate('bar_method', [[10,150,20,1130]])
34
+
35
+ msg.should_not be_nil
36
+ msg.header.target.should == 'bar_method'
37
+ msg.header.type.should == 'request'
38
+ msg.body.fields.size.should == 1
39
+ msg.body.fields[0].name.should == 'byte_array'
40
+ msg.body.fields[0].value.should == '0004000210000315000022000041130'
41
+
42
+ finished_lock = Semaphore.new(1)
43
+ finished_lock.wait()
44
+
45
+ schema_def.methods[1].handler = lambda { |byte_array|
46
+ byte_array.size.should == 4
47
+ byte_array[0].should == 10
48
+ byte_array[1].should == 150
49
+ byte_array[2].should == 20
50
+ byte_array[3].should == 1130
51
+
52
+ x = 0
53
+ byte_array.each { |b| x += b }
54
+ return x > 300 ? 1 : 0
55
+ }
56
+
57
+ server = QpidAdapter::Node.new :id => "server"
58
+ server.async_accept { |node, msg, reply_to|
59
+ mmc.message_received(node, msg, reply_to)
60
+ }
61
+
62
+ client = QpidAdapter::Node.new :id => 'client'
63
+ client.async_accept { |node, msg, reply_to|
64
+ msg = Message::Message.from_s(msg)
65
+ msg.body.fields.size.should == 1
66
+ msg.body.fields[0].name.should == 'bool_success'
67
+ msg.body.fields[0].value.to_i.should == 1
68
+ finished_lock.signal()
69
+ }
70
+ client.send_message("server-queue", msg)
71
+ finished_lock.wait()
72
+
73
+ # FIXME test method that doesn't have any return values
74
+ end
75
+
76
+ it "it should run properly" do
77
+ server = Node.new(:id => "server3", :schema => TEST_SCHEMA)
78
+ client = Node.new(:id => "client3", :schema => TEST_SCHEMA, :destination => "server3")
79
+
80
+ server.handle_method("foo_method") { |some_int, floating_point_number|
81
+ some_int.should == 10
82
+ floating_point_number.should == 15.4
83
+
84
+ ["work_plz", MyClass.new("foobar", 4.2)] # FIXME have to manually return args in an array
85
+ # and can't use return 1,2,3,.. shorthand
86
+ # as currently in ruby, invoking 'return'
87
+ # from a Proc has undesirable behavior
88
+ }
89
+
90
+ a_str, my_class_instance = client.foo_method(10, 15.4)
91
+
92
+ a_str.should == "work_plz"
93
+ my_class_instance.str_member.should == "foobar"
94
+ my_class_instance.float_member.should == 4.2
95
+
96
+ # FIXME test method that doesn't have any return values
97
+ end
98
+
99
+ end
data/spec/qpid_spec.rb ADDED
@@ -0,0 +1,92 @@
1
+ # simrpc qpid adapter spec
2
+ #
3
+ # Copyright (c) 2010 Mohammed Morsi <movitto@yahoo.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person
6
+ # obtaining a copy of this software and associated documentation
7
+ # files (the "Software"), to deal in the Software without
8
+ # restriction, including without limitation the rights to use,
9
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the
11
+ # Software is furnished to do so, subject to the following
12
+ # conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ # OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ require File.dirname(__FILE__) + '/spec_helper'
27
+
28
+ describe "Simrpc::QpidAdapter" do
29
+
30
+ ################## test qpid module
31
+
32
+ # ensure that we can connect to the qpid broker
33
+ it "should connect to broker" do
34
+ # TODO test w/ broker/port & specified conf
35
+ qpid = QpidAdapter::Node.new :id => "test1"
36
+ ssn = qpid.instance_variable_get('@ssn')
37
+ id = qpid.instance_variable_get('@node_id')
38
+ ssn.error?.should == false
39
+ #ssn.closed.should == false
40
+ id.should == "test1"
41
+ end
42
+
43
+ # ensure we define a queue and exchange
44
+ it "should define a queue and exchange" do
45
+ node = QpidAdapter::Node.new :id => "test2"
46
+
47
+ exchange = node.instance_variable_get("@exchange")
48
+ exchange.should == "test2-exchange"
49
+
50
+ queue = node.instance_variable_get("@queue")
51
+ queue.should == "test2-queue"
52
+
53
+ local_queue = node.instance_variable_get("@local_queue")
54
+ local_queue.should == "test2-local-queue"
55
+
56
+ routing_key = node.instance_variable_get("@routing_key")
57
+ routing_key.should == "test2-queue"
58
+
59
+ ssn = node.instance_variable_get('@ssn')
60
+ assert !ssn.exchange_query("test2-exchange").not_found
61
+ assert !ssn.queue_query("test2-queue").queue.nil?
62
+
63
+ # TODO how do I get this:
64
+ # http://www.redhat.com/docs/en-US/Red_Hat_Enterprise_MRG/1.1/html/python/public/qpid.generator.ControlInvoker_0_10-class.html#exchange_bound_result
65
+ #binding_result = ssn.binding_query("test2-queue")
66
+ #assert !binding_result.exchange_not_found?
67
+ #assert !binding_result.queue_not_found?
68
+ #assert !binding_result.queue_not_matched?
69
+ #assert !binding_result.key_not_found?
70
+ end
71
+
72
+ # test sending/receiving a message
73
+ it "should transmit a message" do
74
+ server = QpidAdapter::Node.new :id => "server1"
75
+ server.async_accept { |node, msg, reply_to|
76
+ assert_equal('test-data', msg)
77
+ node.send_message(reply_to, "test-response")
78
+ }
79
+
80
+ finished_lock = Semaphore.new(1)
81
+ finished_lock.wait()
82
+
83
+ client = QpidAdapter::Node.new :id => 'client1'
84
+ client.async_accept { |node, msg, reply_to|
85
+ assert_equal("test-response", msg)
86
+ finished_lock.signal()
87
+ }
88
+ client.send_message("server1-queue", "test-data")
89
+ finished_lock.wait()
90
+ end
91
+
92
+ end
@@ -0,0 +1,348 @@
1
+ # simrpc schema spec
2
+ #
3
+ # Copyright (c) 2010 Mohammed Morsi <movitto@yahoo.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person
6
+ # obtaining a copy of this software and associated documentation
7
+ # files (the "Software"), to deal in the Software without
8
+ # restriction, including without limitation the rights to use,
9
+ # copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the
11
+ # Software is furnished to do so, subject to the following
12
+ # conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19
+ # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21
+ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22
+ # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
+ # OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ require File.dirname(__FILE__) + '/spec_helper'
27
+
28
+ describe "Simrpc::Schema" do
29
+
30
+ it "should correctly parse a simrpc schema" do
31
+ schema_def = Schema::Parser.parse(:schema => TEST_SCHEMA)
32
+
33
+ schema_def.classes.size.should == 1
34
+ schema_def.methods.size.should == 2
35
+
36
+ schema_def.classes[0].name.should == "MyClass"
37
+ schema_def.classes[0].members.size.should == 3
38
+ schema_def.classes[0].members[0].type.should == :str
39
+ schema_def.classes[0].members[0].name.should == 'str_member'
40
+ schema_def.classes[0].members[1].type.should == :float
41
+ schema_def.classes[0].members[1].name.should == 'float_member'
42
+ schema_def.classes[0].members[2].type.should == :obj
43
+ schema_def.classes[0].members[2].name.should == 'associated_obj'
44
+ schema_def.classes[0].members[2].ignore_null.should == true
45
+
46
+ schema_def.methods[0].name.should == "foo_method"
47
+ schema_def.methods[0].parameters.size.should == 2
48
+ schema_def.methods[0].parameters[0].type.should == :int
49
+ schema_def.methods[0].parameters[0].name.should == 'some_int'
50
+ schema_def.methods[0].parameters[1].type.should == :float
51
+ schema_def.methods[0].parameters[1].name.should == 'floating_point_number'
52
+ schema_def.methods[0].return_values.size.should == 2
53
+ schema_def.methods[0].return_values[0].type.should == :str
54
+ schema_def.methods[0].return_values[0].name.should == 'a_string'
55
+ schema_def.methods[0].return_values[1].type.should == :obj
56
+ schema_def.methods[0].return_values[1].name.should == 'my_class_instance'
57
+ schema_def.methods[0].return_values[1].associated.should == :MyClass
58
+ schema_def.methods[0].return_values[1].associated_class_def(schema_def).should == schema_def.classes[0]
59
+
60
+ schema_def.methods[1].name.should == "bar_method"
61
+ schema_def.methods[1].parameters.size.should == 1
62
+ schema_def.methods[1].return_values.size.should == 1
63
+ schema_def.methods[1].parameters[0].type.should == :array
64
+ schema_def.methods[1].parameters[0].name.should == 'byte_array'
65
+ schema_def.methods[1].parameters[0].associated.should == :int
66
+ schema_def.methods[1].return_values[0].type.should == :int
67
+ schema_def.methods[1].return_values[0].name.should == 'bool_success'
68
+ end
69
+
70
+ it "should correct parse class" do
71
+ schema_def = Schema::Parser.parse(:schema =>
72
+ "<schema><class name='Base'></class><class name='Derived' inherits='Base' /></schema>")
73
+
74
+ schema_def.classes.size.should == 2
75
+ schema_def.classes[0].name.should == "Base"
76
+ schema_def.classes[0].inherits.should == nil
77
+ schema_def.classes[1].name.should == "Derived"
78
+ schema_def.classes[1].inherits.should == "Base"
79
+ end
80
+
81
+ it "should correctly identify primitives" do
82
+ Schema::is_primitive?(:int).should == true
83
+ Schema::is_primitive?(:float).should == true
84
+ Schema::is_primitive?(:str).should == true
85
+ !Schema::is_primitive?(:obj).should == true
86
+ !Schema::is_primitive?(:array).should == true
87
+
88
+ Schema::primitive_from_str(:str, "yo").should == "yo"
89
+ Schema::primitive_from_str(:int, "420").should == 420
90
+ Schema::primitive_from_str(:float, "42.05").should == 42.05
91
+ end
92
+
93
+ it "should convert a data field to / from a string" do
94
+ schema_def = Schema::Parser.parse(:schema => TEST_SCHEMA)
95
+
96
+ data_field = Schema::DataFieldDef.new :name => "foo", :type => :str
97
+ data_field.to_s("bar").should == "bar"
98
+ data_field.from_s("bar").should == "bar"
99
+ data_field.type = :int
100
+ data_field.to_s(420).should == "420"
101
+ data_field.from_s("420").should == 420
102
+ data_field.type = :float
103
+ data_field.to_s(15.4).should == "15.4"
104
+ data_field.from_s("15.4").should == 15.4
105
+ data_field.type = :bool
106
+ data_field.to_s(true).should == "true"
107
+ data_field.from_s("true").should == true
108
+ data_field.to_s(false).should == "false"
109
+ data_field.from_s("false").should == false
110
+
111
+ my_class = MyClass.new("abc", 1.23)
112
+ my_class2 = MyClass.new("def", 4.56)
113
+ my_class_s = schema_def.classes[0].to_s(my_class, schema_def)
114
+ my_class2_s = schema_def.classes[0].to_s(my_class2, schema_def)
115
+ my_class_o = schema_def.classes[0].from_s(my_class_s, schema_def)
116
+ my_class2_o = schema_def.classes[0].from_s(my_class2_s, schema_def)
117
+
118
+ data_field.type = :obj
119
+ data_field.associated = "MyClass"
120
+ data_field.to_s(my_class, schema_def).should == my_class_s
121
+ #assert_equal my_class_o,
122
+ # data_field.from_s(my_class_s, schema_def)
123
+ my_class_o.str_member.should == "abc"
124
+ my_class_o.float_member.should == 1.23
125
+
126
+ data_field.type = :array
127
+ array_s = data_field.to_s([my_class, my_class2], schema_def)
128
+ array_s[0...4].should == "0002"
129
+ array_s[4...8].should == "%04d" % my_class_s.size
130
+ end_pos = 8+my_class_s.size
131
+ array_s[8...end_pos].should == my_class_s
132
+ array_s[end_pos...end_pos+4].should == "%04d" % my_class2_s.size
133
+ array_s[end_pos+4...end_pos+4+my_class2_s.size].should == my_class2_s
134
+
135
+ array_o = data_field.from_s(array_s, schema_def)
136
+ array_o.size.should == 2
137
+ #assert_equal my_class_o, array_o[0]
138
+ #assert_equal my_class2_o, array_o[1]
139
+ array_o[0].str_member.should == "abc"
140
+ array_o[1].str_member.should == "def"
141
+ array_o[0].float_member.should == 1.23
142
+ array_o[1].float_member.should == 4.56
143
+
144
+ data_field.associated = :str
145
+ array_s = data_field.to_s(['abc', 'def', 'hijklmnopqrstuvwxyz123456789'])
146
+ array_s[0...4].should == "0003"
147
+ array_s[4...8].should == "0003"
148
+ array_s[8...11].should == "abc"
149
+ array_s[11...15].should == "0003"
150
+ array_s[15...18].should == "def"
151
+ array_s[18...22].should == "0028"
152
+ array_s[22...50].should == "hijklmnopqrstuvwxyz123456789"
153
+
154
+ array_o = data_field.from_s(array_s)
155
+ array_o.size.should == 3
156
+ array_o[0].should == "abc"
157
+ array_o[1].should == "def"
158
+ array_o[2].should == "hijklmnopqrstuvwxyz123456789"
159
+
160
+ array_s = data_field.to_s([])
161
+ array_s[0...4].should == "0000"
162
+ array_o = data_field.from_s(array_s)
163
+ array_o.size.should == 0
164
+ end
165
+
166
+ it "should raise an error if a field refers to an invalid class" do
167
+ schema_def = Schema::Parser.parse(:schema =>
168
+ "<schema>" +
169
+ "<class name='SchemaTestSuper'>"+
170
+ "<member type='obj' name='super_attr' />" +
171
+ "</class>" +
172
+ "</schema>")
173
+
174
+ lambda {
175
+ schema_def.classes[0].members[0].to_s(SchemaTestBase.new, schema_def) # try converting a class not defined in schema to_s
176
+ }.should raise_error(InvalidSchemaClass, "cannot find SchemaTestBase in schema")
177
+
178
+ lambda {
179
+ schema_def.classes[0].members[0].from_s("0014SchemaTestBase", schema_def)
180
+ }.should raise_error(InvalidSchemaClass, "cannot find SchemaTestBase in schema")
181
+
182
+
183
+ schema_def = Schema::Parser.parse(:schema =>
184
+ "<schema>" +
185
+ "<class name='SchemaTestSuper'>"+
186
+ "<member type='obj' associated='SchemaTestDerived' name='super_attr' />" + # associate w/ class not defined in schema
187
+ "</class>" +
188
+ "</schema>")
189
+
190
+ lambda {
191
+ schema_def.classes[0].members[0].to_s(SchemaTestDerived.new, schema_def)
192
+ }.should raise_error(InvalidSchemaClass, "cannot find SchemaTestDerived in schema")
193
+
194
+ lambda {
195
+ schema_def.classes[0].members[0].from_s("0017SchemaTestDerived", schema_def)
196
+ }.should raise_error(InvalidSchemaClass, "cannot find SchemaTestDerived in schema")
197
+ end
198
+
199
+ it "should convert a class to / from a string" do
200
+ schema_def = Schema::Parser.parse(:schema => TEST_SCHEMA)
201
+ my_class = MyClass.new('abc', 12.3)
202
+ my_class_s = schema_def.classes[0].to_s(my_class, schema_def)
203
+
204
+ my_class_s.size.should == 20
205
+ my_class_s[0].chr.should == "I" # object defined here, not refrence to elsewhere
206
+ my_class_s[1...5].should == "0003" # length of the first member
207
+ my_class_s[5...8].should == "abc" # first member in string format
208
+ my_class_s[8...12].should == "0004" # length of second member
209
+ my_class_s[12...16].should == "12.3" # second member in string format
210
+ my_class_s[16...20].should == "0000" # length of last class member (since we're not setting in this case, 0)
211
+
212
+ my_class_o = schema_def.classes[0].from_s(my_class_s, schema_def)
213
+ my_class_o.class.should == MyClass
214
+ my_class_o.str_member.should == "abc"
215
+ my_class_o.float_member.should == 12.3
216
+ my_class_o.associated_obj.should == nil
217
+ my_class_o.associated_obj_set.should == false # test ignore_null works
218
+
219
+ schema_def.classes[0].to_s(nil).should == "I"
220
+ schema_def.classes[0].from_s("I").should == nil
221
+
222
+ # test MyClass w/ associated_obj member that has no 'associated' attribute
223
+ my_class = MyClass.new('abc', 12.3, MyClass.new('foo'))
224
+ my_class_s = schema_def.classes[0].to_s(my_class, schema_def)
225
+ my_class_s.size.should == 50
226
+ my_class_s.should == "I" + "0003" + "abc" + "0004" + "12.3" + "0030" + "0007" + "MyClass" + "I" + "0003" + "foo" + "0003" + "0.0" + "0000"
227
+
228
+ my_class_o = schema_def.classes[0].from_s(my_class_s, schema_def)
229
+ my_class_o.str_member.should == "abc"
230
+ my_class_o.float_member.should == 12.3
231
+ my_class_o.associated_obj.class.should == MyClass
232
+ my_class_o.associated_obj.str_member.should == "foo"
233
+ my_class_o.associated_obj.float_member.should == 0
234
+ my_class_o.associated_obj_set.should == true # test ignore_null works
235
+
236
+ # FIXME test recursion as implemented w/ the converted_classes
237
+ # parameters to to_s & from_s
238
+ end
239
+
240
+ it "should convert an inherited class to / from a string" do
241
+ schema_def = Schema::Parser.parse(:schema =>
242
+ "<schema>" +
243
+ "<class name='SchemaTestSuper'>"+
244
+ "<member type='str' name='super_attr' />" +
245
+ "</class>" +
246
+ "<class name='SchemaTestBase' inherits='SchemaTestSuper' >"+
247
+ "<member type='str' name='base_attr' />" +
248
+ "</class>" +
249
+ "<class name='SchemaTestDerived' inherits='SchemaTestBase'>" +
250
+ "<member type='str' name='derived_attr' />" +
251
+ "</class>" +
252
+ "</schema>")
253
+
254
+ derived = SchemaTestDerived.new
255
+ derived.derived_attr = "foo"
256
+ derived.base_attr = "bar"
257
+ derived.super_attr = "superman"
258
+
259
+ ds = schema_def.classes[2].to_s(derived, schema_def)
260
+ ds.should == "I" + "0003" + "foo" + "0003" + "bar" + "0008" + "superman"
261
+
262
+ dso = schema_def.classes[2].from_s(ds, schema_def)
263
+ dso.derived_attr.should == "foo"
264
+ dso.base_attr.should == "bar"
265
+ dso.super_attr.should == "superman"
266
+ end
267
+
268
+ it "should convert a class w/ generic object attribute to / from string" do
269
+ schema_def = Schema::Parser.parse(:schema =>
270
+ "<schema>" +
271
+ "<class name='SchemaTestSuper'>"+
272
+ "<member type='obj' name='super_attr' />" +
273
+ "<member type='str' name='another_attr' />" +
274
+ "</class>" +
275
+ "</schema>")
276
+
277
+ sup1 = SchemaTestSuper.new
278
+ sup2 = SchemaTestSuper.new
279
+ sup2.another_attr = "foobar"
280
+ sup1.super_attr = sup2
281
+
282
+ ss = schema_def.classes[0].to_s(sup1, schema_def)
283
+ ss.should == "I" + "0034" + "0015" + "SchemaTestSuper" + "I" + "0000" + "0006" + "foobar" + "0000"
284
+
285
+ sso = schema_def.classes[0].from_s(ss, schema_def)
286
+ sso.super_attr.class.should == SchemaTestSuper
287
+ sso.super_attr.another_attr.should == "foobar"
288
+
289
+ sup1.super_attr = nil
290
+ sup1.another_attr = "money"
291
+ ss = schema_def.classes[0].to_s(sup1, schema_def)
292
+ ss.should == "I" + "0000" + "0005" + "money"
293
+
294
+ sso = schema_def.classes[0].from_s(ss, schema_def)
295
+ sso.super_attr.should == nil
296
+ end
297
+
298
+ it "should be able to create singleton class from string" do
299
+ schema_def = Schema::Parser.parse(:schema =>
300
+ "<schema>" +
301
+ "<class name='StandardSingletonTest'>"+
302
+ "<member type='str' name='an_attr' />" +
303
+ "</class>" +
304
+ "</schema>")
305
+
306
+ lambda {
307
+ schema_def.classes[0].from_s("I0003foo", schema_def)
308
+ }.should_not raise_error
309
+ end
310
+
311
+ it "should raise an error if it fails to create a new object from string" do
312
+ schema_def = Schema::Parser.parse(:schema =>
313
+ "<schema>" +
314
+ "<class name='CustomSingletonTest'>"+
315
+ "<member type='str' name='an_attr' />" +
316
+ "</class>" +
317
+ "</schema>")
318
+
319
+ lambda {
320
+ schema_def.classes[0].from_s("I0000", schema_def)
321
+ }.should raise_error(InvalidSchemaClass, "cannot create schema class CustomSingletonTest")
322
+ end
323
+
324
+
325
+ end
326
+
327
+ class SchemaTestSuper
328
+ attr_accessor :super_attr
329
+ attr_accessor :another_attr
330
+ end
331
+
332
+ class SchemaTestBase < SchemaTestSuper
333
+ attr_accessor :base_attr
334
+ end
335
+
336
+ class SchemaTestDerived < SchemaTestBase
337
+ attr_accessor :derived_attr
338
+ end
339
+
340
+ class StandardSingletonTest
341
+ include Singleton
342
+ end
343
+
344
+ # test class where 'new' a 'instance' are not public
345
+ class CustomSingletonTest
346
+ attr_accessor :an_attr
347
+ private_class_method :new
348
+ end