simrpc 0.1 → 0.2

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.
@@ -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