rubyslim-unofficial 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|