rubyslim-unofficial 0.0.1
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/.gitignore +7 -0
- data/Gemfile +2 -0
- data/README.md +187 -0
- data/Rakefile +21 -0
- data/bin/rubyslim +10 -0
- data/lib/rubyslim/list_deserializer.rb +69 -0
- data/lib/rubyslim/list_executor.rb +12 -0
- data/lib/rubyslim/list_serializer.rb +45 -0
- data/lib/rubyslim/ruby_slim.rb +61 -0
- data/lib/rubyslim/slim_error.rb +3 -0
- data/lib/rubyslim/slim_helper_library.rb +23 -0
- data/lib/rubyslim/socket_service.rb +53 -0
- data/lib/rubyslim/statement.rb +79 -0
- data/lib/rubyslim/statement_executor.rb +174 -0
- data/lib/rubyslim/table_to_hash_converter.rb +32 -0
- data/lib/test_module/library_new.rb +13 -0
- data/lib/test_module/library_old.rb +12 -0
- data/lib/test_module/should_not_find_test_slim_in_here/test_slim.rb +7 -0
- data/lib/test_module/simple_script.rb +9 -0
- data/lib/test_module/test_chain.rb +5 -0
- data/lib/test_module/test_slim.rb +86 -0
- data/lib/test_module/test_slim_with_arguments.rb +23 -0
- data/lib/test_module/test_slim_with_no_sut.rb +5 -0
- data/rubyslim-unofficial.gemspec +25 -0
- data/spec/instance_creation_spec.rb +40 -0
- data/spec/it8f_spec.rb +8 -0
- data/spec/list_deserializer_spec.rb +66 -0
- data/spec/list_executor_spec.rb +187 -0
- data/spec/list_serialzer_spec.rb +38 -0
- data/spec/method_invocation_spec.rb +127 -0
- data/spec/slim_helper_library_spec.rb +80 -0
- data/spec/socket_service_spec.rb +98 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/statement_executor_spec.rb +50 -0
- data/spec/statement_spec.rb +17 -0
- data/spec/table_to_hash_converter_spec.rb +31 -0
- metadata +132 -0
@@ -0,0 +1,174 @@
|
|
1
|
+
require "rubyslim/slim_error"
|
2
|
+
require "rubyslim/statement"
|
3
|
+
require "rubyslim/table_to_hash_converter"
|
4
|
+
require "rubyslim/slim_helper_library"
|
5
|
+
|
6
|
+
class StatementExecutor
|
7
|
+
def initialize
|
8
|
+
@instances = {}
|
9
|
+
@modules = []
|
10
|
+
@symbols = {}
|
11
|
+
@libraries = [SlimHelperLibrary.new(self)]
|
12
|
+
end
|
13
|
+
|
14
|
+
def library?(instance_name)
|
15
|
+
library_prefix = "library"
|
16
|
+
instance_name[0, library_prefix.length] == library_prefix
|
17
|
+
end
|
18
|
+
|
19
|
+
def create(instance_name, class_name, constructor_arguments)
|
20
|
+
begin
|
21
|
+
instance = replace_symbol(class_name)
|
22
|
+
if instance.is_a?(String)
|
23
|
+
instance = construct_instance(instance, replace_symbols(constructor_arguments))
|
24
|
+
if library?(instance_name)
|
25
|
+
@libraries << instance
|
26
|
+
end
|
27
|
+
end
|
28
|
+
set_instance(instance_name, instance)
|
29
|
+
"OK"
|
30
|
+
rescue SlimError => e
|
31
|
+
Statement::EXCEPTION_TAG + e.to_s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_instance(instance_name, instance)
|
36
|
+
@instances[instance_name] = instance
|
37
|
+
end
|
38
|
+
|
39
|
+
def construct_instance(class_name, constructor_arguments)
|
40
|
+
require_class(class_name);
|
41
|
+
construct(class_name, constructor_arguments);
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def make_path_to_class(class_name)
|
46
|
+
module_names = split_class_name(class_name)
|
47
|
+
files = module_names.collect { |module_name| to_file_name(module_name) }
|
48
|
+
File.join(files)
|
49
|
+
end
|
50
|
+
|
51
|
+
def split_class_name(class_name)
|
52
|
+
class_name.split(/\:\:/)
|
53
|
+
end
|
54
|
+
|
55
|
+
def replace_tables_with_hashes(constructor_arguments)
|
56
|
+
args = constructor_arguments.map do |arg|
|
57
|
+
TableToHashConverter.convert arg
|
58
|
+
end
|
59
|
+
return args
|
60
|
+
end
|
61
|
+
|
62
|
+
def construct(class_name, constructor_arguments)
|
63
|
+
class_object = get_class(class_name)
|
64
|
+
begin
|
65
|
+
class_object.new(*replace_tables_with_hashes(constructor_arguments))
|
66
|
+
rescue ArgumentError => e
|
67
|
+
raise SlimError.new("message:<<COULD_NOT_INVOKE_CONSTRUCTOR #{class_name}[#{constructor_arguments.length}]>>")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def with_each_fully_qualified_class_name(class_name, &block)
|
73
|
+
(@modules.map { |module_name| module_name + "::" + class_name } << class_name).reverse.each &block
|
74
|
+
end
|
75
|
+
|
76
|
+
def require_class(class_name)
|
77
|
+
with_each_fully_qualified_class_name(class_name) do |fully_qualified_name|
|
78
|
+
begin
|
79
|
+
require make_path_to_class(fully_qualified_name)
|
80
|
+
return
|
81
|
+
rescue LoadError
|
82
|
+
end
|
83
|
+
end
|
84
|
+
raise SlimError.new("message:<<COULD_NOT_INVOKE_CONSTRUCTOR #{class_name} failed to find in #{@modules.map { |mod| make_path_to_class(mod) }.inspect}>>")
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_class(class_name)
|
88
|
+
with_each_fully_qualified_class_name(class_name) do |fully_qualified_name|
|
89
|
+
begin
|
90
|
+
return eval(fully_qualified_name)
|
91
|
+
rescue NameError
|
92
|
+
end
|
93
|
+
end
|
94
|
+
raise SlimError.new("message:<<COULD_NOT_INVOKE_CONSTRUCTOR #{class_name} in any module #{@modules.inspect}>>")
|
95
|
+
end
|
96
|
+
|
97
|
+
def instance(instance_name)
|
98
|
+
@instances[instance_name]
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_file_name(module_name)
|
102
|
+
value = module_name[0..0].downcase + module_name[1..-1]
|
103
|
+
value.gsub(/[A-Z]/) { |cap| "_#{cap.downcase}" }
|
104
|
+
end
|
105
|
+
|
106
|
+
def send_message_to_instance(instance, method, args)
|
107
|
+
symbols = replace_symbols(args)
|
108
|
+
instance.send(method, *replace_tables_with_hashes(symbols))
|
109
|
+
end
|
110
|
+
|
111
|
+
def method_to_call(instance, method_name)
|
112
|
+
return nil unless instance
|
113
|
+
return method_name.to_sym if instance.respond_to?(method_name)
|
114
|
+
return "#{$1}=".to_sym if method_name =~ /set_(\w+)/ && instance.respond_to?("#{$1}=")
|
115
|
+
return nil
|
116
|
+
end
|
117
|
+
|
118
|
+
def call(instance_name, method_name, *args)
|
119
|
+
begin
|
120
|
+
instance = self.instance(instance_name)
|
121
|
+
if method = method_to_call(instance, method_name)
|
122
|
+
send_message_to_instance(instance, method, args)
|
123
|
+
elsif instance.respond_to?(:sut) && method = method_to_call(instance.sut, method_name)
|
124
|
+
send_message_to_instance(instance.sut, method, args)
|
125
|
+
else
|
126
|
+
@libraries.reverse_each do |library|
|
127
|
+
method = method_to_call(library, method_name)
|
128
|
+
return send_message_to_instance(library, method, args) if method
|
129
|
+
end
|
130
|
+
raise SlimError.new("message:<<NO_INSTANCE #{instance_name}>>") if instance.nil?
|
131
|
+
raise SlimError.new("message:<<NO_METHOD_IN_CLASS #{method_name}[#{args.length}] #{instance.class.name}.>>")
|
132
|
+
end
|
133
|
+
rescue SlimError => e
|
134
|
+
Statement::EXCEPTION_TAG + e.to_s
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def add_module(module_name)
|
139
|
+
@modules << module_name.gsub(/\./, '::')
|
140
|
+
end
|
141
|
+
|
142
|
+
def set_symbol(name, value)
|
143
|
+
@symbols[name] = value
|
144
|
+
end
|
145
|
+
|
146
|
+
def get_symbol(name)
|
147
|
+
@symbols[name]
|
148
|
+
end
|
149
|
+
|
150
|
+
def acquire_symbol(symbol_text)
|
151
|
+
symbol = get_symbol(symbol_text[1..-1])
|
152
|
+
symbol = symbol_text if symbol.nil?
|
153
|
+
symbol
|
154
|
+
end
|
155
|
+
|
156
|
+
def replace_symbol(item)
|
157
|
+
match = item.match(/\A\$\w*\z/)
|
158
|
+
return acquire_symbol(match[0]) if match
|
159
|
+
|
160
|
+
item.gsub(/\$\w*/) do |match|
|
161
|
+
acquire_symbol(match)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def replace_symbols(list)
|
166
|
+
list.map do |item|
|
167
|
+
if item.kind_of?(Array)
|
168
|
+
replace_symbols(item)
|
169
|
+
else
|
170
|
+
replace_symbol(item)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
class TableToHashConverter
|
4
|
+
def self.convert(string)
|
5
|
+
begin
|
6
|
+
doc = REXML::Document.new(string.strip)
|
7
|
+
return html_to_hash(doc)
|
8
|
+
rescue
|
9
|
+
return string
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.html_to_hash(doc)
|
14
|
+
table = doc.elements['table']
|
15
|
+
raise ArgumentError if table.nil?
|
16
|
+
create_hash_from_rows(table)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.create_hash_from_rows(rows)
|
20
|
+
hash = {}
|
21
|
+
rows.elements.each('tr') do |row|
|
22
|
+
add_row_to_hash(hash, row)
|
23
|
+
end
|
24
|
+
hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.add_row_to_hash(hash, row)
|
28
|
+
columns = row.get_elements('td')
|
29
|
+
raise ArgumentError if columns.size != 2
|
30
|
+
hash[columns[0].text.strip.to_sym] = columns[1].text.strip
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module TestModule
|
4
|
+
class SystemUnderTest
|
5
|
+
attr_accessor :attribute
|
6
|
+
def sut_method
|
7
|
+
true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class TestSlim
|
12
|
+
attr_reader :sut
|
13
|
+
attr_accessor :string
|
14
|
+
|
15
|
+
def initialize(generation = 0)
|
16
|
+
@generation = generation
|
17
|
+
@sut = SystemUnderTest.new
|
18
|
+
@string = "string"
|
19
|
+
end
|
20
|
+
|
21
|
+
def return_string
|
22
|
+
@string
|
23
|
+
end
|
24
|
+
|
25
|
+
def returnString #Should not ever be called.
|
26
|
+
"blah"
|
27
|
+
end
|
28
|
+
|
29
|
+
def add(a, b)
|
30
|
+
a+b
|
31
|
+
end
|
32
|
+
|
33
|
+
def echo(x)
|
34
|
+
x
|
35
|
+
end
|
36
|
+
|
37
|
+
def null
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def echo_int i
|
42
|
+
i
|
43
|
+
end
|
44
|
+
|
45
|
+
def echo_string s
|
46
|
+
s
|
47
|
+
end
|
48
|
+
|
49
|
+
def syntax_error
|
50
|
+
eval "1,2"
|
51
|
+
end
|
52
|
+
|
53
|
+
def utf8
|
54
|
+
"Espa\357\277\275ol"
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_test_slim_with_string(string)
|
58
|
+
slim = TestSlim.new(@generation + 1)
|
59
|
+
slim.string = string
|
60
|
+
slim
|
61
|
+
end
|
62
|
+
|
63
|
+
def new_with_string(string)
|
64
|
+
s = TestSlim.new
|
65
|
+
s.string = string
|
66
|
+
s
|
67
|
+
end
|
68
|
+
|
69
|
+
def echo_object(method, string)
|
70
|
+
OpenStruct.new(method.to_sym => string)
|
71
|
+
end
|
72
|
+
|
73
|
+
def call_on(method, object)
|
74
|
+
object.send(method.to_sym)
|
75
|
+
end
|
76
|
+
|
77
|
+
# def is_same(other)
|
78
|
+
# self === other
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# def get_string_from_other other
|
82
|
+
# other.get_string_arg
|
83
|
+
# end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module TestModule
|
2
|
+
class TestSlimWithArguments
|
3
|
+
def initialize(arg)
|
4
|
+
@arg = arg
|
5
|
+
end
|
6
|
+
|
7
|
+
def arg
|
8
|
+
@arg
|
9
|
+
end
|
10
|
+
|
11
|
+
def name
|
12
|
+
@arg[:name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def addr
|
16
|
+
@arg[:addr]
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_arg(hash)
|
20
|
+
@arg = hash
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = "rubyslim-unofficial"
|
4
|
+
s.version = "0.0.1"
|
5
|
+
s.summary = "Ruby SliM protocol for FitNesse"
|
6
|
+
s.description = <<-EOS
|
7
|
+
RubySliM implements the SliM protocol for the FitNesse acceptance testing
|
8
|
+
framework. This gem is an unofficial version created and distributed by
|
9
|
+
Eric Pierce <wapcaplet88@gmail.com>, based on the original code by Robert
|
10
|
+
C. Martin and Doug Bradbury.
|
11
|
+
EOS
|
12
|
+
s.authors = ["Robert C. Martin", "Doug Bradbury"]
|
13
|
+
s.email = "unclebob@cleancoder.com"
|
14
|
+
s.homepage = "http://github.com/wapcaplet/rubyslim"
|
15
|
+
s.platform = Gem::Platform::RUBY
|
16
|
+
|
17
|
+
s.add_development_dependency 'rspec', '~> 1.3.0'
|
18
|
+
s.add_development_dependency 'rcov'
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.require_path = 'lib'
|
22
|
+
|
23
|
+
s.executables = ['rubyslim']
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
|
2
|
+
require "statement_executor"
|
3
|
+
|
4
|
+
describe StatementExecutor do
|
5
|
+
before do
|
6
|
+
@caller = StatementExecutor.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "can create an instance" do
|
10
|
+
response = @caller.create("x", "TestModule::TestSlim", [])
|
11
|
+
response.should == "OK"
|
12
|
+
x = @caller.instance("x")
|
13
|
+
x.class.name.should == "TestModule::TestSlim"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "can create an instance with arguments" do
|
17
|
+
response = @caller.create("x", "TestModule::TestSlimWithArguments", ["3"])
|
18
|
+
response.should == "OK"
|
19
|
+
x = @caller.instance("x")
|
20
|
+
x.arg.should == "3"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "can create an instance with arguments that are symbols" do
|
24
|
+
@caller.set_symbol("X", "3")
|
25
|
+
response = @caller.create("x", "TestModule::TestSlimWithArguments", ["$X"])
|
26
|
+
response.should == "OK"
|
27
|
+
x = @caller.instance("x")
|
28
|
+
x.arg.should == "3"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "can't create an instance with the wrong number of arguments" do
|
32
|
+
result = @caller.create("x", "TestModule::TestSlim", ["1", "noSuchArgument"])
|
33
|
+
result.should include(Statement::EXCEPTION_TAG + "message:<<COULD_NOT_INVOKE_CONSTRUCTOR TestModule::TestSlim[2]>>")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "can't create an instance if there is no class" do
|
37
|
+
result = @caller.create("x", "TestModule::NoSuchClass", [])
|
38
|
+
result.should include(Statement::EXCEPTION_TAG + "message:<<COULD_NOT_INVOKE_CONSTRUCTOR TestModule::NoSuchClass failed to find in []>>")
|
39
|
+
end
|
40
|
+
end
|
data/spec/it8f_spec.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
|
2
|
+
require "list_serializer"
|
3
|
+
require "list_deserializer"
|
4
|
+
|
5
|
+
describe ListDeserializer do
|
6
|
+
before do
|
7
|
+
@list = []
|
8
|
+
end
|
9
|
+
|
10
|
+
it "can't deserialize a null string" do
|
11
|
+
proc {ListDeserializer.deserialize(nil)}.should raise_error(ListDeserializer::SyntaxError)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can't deserialize empty string" do
|
15
|
+
proc {ListDeserializer.deserialize("")}.should raise_error(ListDeserializer::SyntaxError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "can't deserialize string that doesn't start with an open bracket" do
|
19
|
+
proc {ListDeserializer.deserialize("hello")}.should raise_error(ListDeserializer::SyntaxError)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "can't deserialize string that doesn't end with a bracket" do
|
23
|
+
proc {ListDeserializer.deserialize("[000000:")}.should raise_error(ListDeserializer::SyntaxError)
|
24
|
+
end
|
25
|
+
|
26
|
+
def check()
|
27
|
+
serialized = ListSerializer.serialize(@list)
|
28
|
+
deserialized = ListDeserializer.deserialize(serialized)
|
29
|
+
deserialized.should == @list
|
30
|
+
end
|
31
|
+
|
32
|
+
it "can deserialize and empty list" do
|
33
|
+
check
|
34
|
+
end
|
35
|
+
|
36
|
+
it "can deserialize a list with one element" do
|
37
|
+
@list = ["hello"]
|
38
|
+
check
|
39
|
+
end
|
40
|
+
|
41
|
+
it "can deserialize a list with two elements" do
|
42
|
+
@list = ["hello", "bob"]
|
43
|
+
check
|
44
|
+
end
|
45
|
+
|
46
|
+
it "can deserialize sublists" do
|
47
|
+
@list = ["hello", ["bob", "micah"], "today"]
|
48
|
+
check
|
49
|
+
end
|
50
|
+
|
51
|
+
it "can deserialize lists with multibyte strings" do
|
52
|
+
@list = ["Köln"]
|
53
|
+
check
|
54
|
+
end
|
55
|
+
|
56
|
+
it "can deserialize lists of strings that end with multibyte chars" do
|
57
|
+
@list = ["Kö"]
|
58
|
+
check
|
59
|
+
end
|
60
|
+
|
61
|
+
it "can deserialize lists with utf8" do
|
62
|
+
@list = ["123456789012345", "Espa\357\277\275ol"]
|
63
|
+
check
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|