kronk 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/History.rdoc +22 -0
- data/Manifest.txt +15 -6
- data/README.rdoc +5 -6
- data/Rakefile +5 -5
- data/lib/kronk.rb +164 -194
- data/lib/kronk/cmd.rb +188 -74
- data/lib/kronk/constants.rb +90 -0
- data/lib/kronk/data_renderer.rb +146 -0
- data/lib/kronk/diff.rb +4 -92
- data/lib/kronk/path/transaction.rb +2 -0
- data/lib/kronk/player.rb +233 -0
- data/lib/kronk/player/benchmark.rb +261 -0
- data/lib/kronk/player/input_reader.rb +54 -0
- data/lib/kronk/player/output.rb +49 -0
- data/lib/kronk/player/request_parser.rb +24 -0
- data/lib/kronk/player/stream.rb +50 -0
- data/lib/kronk/player/suite.rb +123 -0
- data/lib/kronk/plist_parser.rb +4 -0
- data/lib/kronk/request.rb +265 -241
- data/lib/kronk/response.rb +330 -149
- data/lib/kronk/test/assertions.rb +2 -2
- data/lib/kronk/xml_parser.rb +7 -1
- data/test/mocks/cookies.yml +32 -0
- data/test/mocks/get_request.txt +6 -0
- data/test/test_assertions.rb +6 -6
- data/test/test_cmd.rb +708 -0
- data/test/test_diff.rb +210 -75
- data/test/test_helper.rb +140 -0
- data/test/test_helper_methods.rb +4 -4
- data/test/test_input_reader.rb +103 -0
- data/test/test_kronk.rb +142 -141
- data/test/test_player.rb +589 -0
- data/test/test_request.rb +147 -212
- data/test/test_request_parser.rb +31 -0
- data/test/test_response.rb +206 -15
- metadata +41 -74
- data/bin/yzma +0 -13
- data/lib/kronk/data_set.rb +0 -144
- data/lib/yzma.rb +0 -174
- data/lib/yzma/randomizer.rb +0 -54
- data/lib/yzma/report.rb +0 -47
- data/test/test_data_set.rb +0 -213
@@ -0,0 +1,146 @@
|
|
1
|
+
class Kronk
|
2
|
+
|
3
|
+
##
|
4
|
+
# Creates ordered data strings for rendering to the output.
|
5
|
+
|
6
|
+
module DataRenderer
|
7
|
+
|
8
|
+
##
|
9
|
+
# Returns a ruby data string that is diff-able, meaning sorted by
|
10
|
+
# Hash keys when available.
|
11
|
+
|
12
|
+
def self.ruby data, struct_only=false
|
13
|
+
ordered_data_string data, struct_only do |type, obj|
|
14
|
+
case type
|
15
|
+
when :key_assign then " =>"
|
16
|
+
when :key then obj.inspect
|
17
|
+
when :value then obj.inspect
|
18
|
+
when :struct
|
19
|
+
(obj == true || obj == false) ? "Boolean" : obj.class
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Returns a json data string that is diff-able, meaning sorted by
|
26
|
+
# Hash keys when available.
|
27
|
+
|
28
|
+
def self.json data, struct_only=false
|
29
|
+
ordered_data_string data, struct_only do |type, obj|
|
30
|
+
case type
|
31
|
+
when :key_assign then ":"
|
32
|
+
when :key
|
33
|
+
(Symbol === obj ? obj.inspect : obj.to_s).to_json
|
34
|
+
when :value
|
35
|
+
(Symbol === obj ? obj.inspect : obj).to_json
|
36
|
+
when :struct
|
37
|
+
((obj == true || obj == false) ? "Boolean" : obj.class).to_json
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def self.ordered_data_string data, struct_only=false, indent=nil, &block
|
44
|
+
i_width = Kronk.config[:indentation] || 1
|
45
|
+
indent ||= 0
|
46
|
+
indent += i_width
|
47
|
+
|
48
|
+
case data
|
49
|
+
|
50
|
+
when Hash
|
51
|
+
return "{}" if data.empty?
|
52
|
+
|
53
|
+
output = "{\n"
|
54
|
+
|
55
|
+
sorted_keys = sort_any data.keys
|
56
|
+
|
57
|
+
data_values =
|
58
|
+
sorted_keys.map do |key|
|
59
|
+
value = data[key]
|
60
|
+
pad = " " * indent
|
61
|
+
subdata = ordered_data_string value, struct_only, indent, &block
|
62
|
+
"#{pad}#{ yield(:key, key) }#{ yield(:key_assign) } #{subdata}"
|
63
|
+
end
|
64
|
+
|
65
|
+
output << data_values.join(",\n") << "\n"
|
66
|
+
output << "#{" " * (indent - i_width)}}"
|
67
|
+
|
68
|
+
when Array
|
69
|
+
return "[]" if data.empty?
|
70
|
+
|
71
|
+
output = "[\n"
|
72
|
+
|
73
|
+
data_values =
|
74
|
+
data.map do |value|
|
75
|
+
pad = " " * indent
|
76
|
+
"#{pad}#{ordered_data_string value, struct_only, indent, &block}"
|
77
|
+
end
|
78
|
+
|
79
|
+
output << data_values.join(",\n") << "\n"
|
80
|
+
output << "#{" " * (indent - i_width)}]"
|
81
|
+
|
82
|
+
else
|
83
|
+
struct_only ? yield(:struct, data) : yield(:value, data)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
|
89
|
+
##
|
90
|
+
# Sorts an array of any combination of string, integer, or symbols.
|
91
|
+
|
92
|
+
def self.sort_any arr
|
93
|
+
i = 1
|
94
|
+
until i >= arr.length
|
95
|
+
j = i-1
|
96
|
+
val = arr[i]
|
97
|
+
prev_val = arr[j]
|
98
|
+
|
99
|
+
loop do
|
100
|
+
if smaller?(val, arr[j])
|
101
|
+
arr[j+1] = arr[j]
|
102
|
+
j = j - 1
|
103
|
+
break if j < 0
|
104
|
+
|
105
|
+
else
|
106
|
+
break
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
arr[j+1] = val
|
111
|
+
|
112
|
+
i = i.next
|
113
|
+
end
|
114
|
+
|
115
|
+
arr
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
##
|
120
|
+
# Compares Numerics, Strings, and Symbols and returns true if the left
|
121
|
+
# side is 'smaller' than the right side.
|
122
|
+
|
123
|
+
def self.smaller? left, right
|
124
|
+
case left
|
125
|
+
when Numeric
|
126
|
+
case right
|
127
|
+
when Numeric then right > left
|
128
|
+
else true
|
129
|
+
end
|
130
|
+
|
131
|
+
when Symbol
|
132
|
+
case right
|
133
|
+
when Numeric then false
|
134
|
+
when Symbol then right.to_s > left.to_s
|
135
|
+
else true
|
136
|
+
end
|
137
|
+
|
138
|
+
when String
|
139
|
+
case right
|
140
|
+
when String then right > left
|
141
|
+
else false
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/lib/kronk/diff.rb
CHANGED
@@ -19,99 +19,11 @@ class Kronk
|
|
19
19
|
# Returns a data string that is diff-able, meaning sorted by
|
20
20
|
# Hash keys when available.
|
21
21
|
|
22
|
-
def self.ordered_data_string data, struct_only=false
|
23
|
-
case
|
24
|
-
|
25
|
-
when Hash
|
26
|
-
output = "{\n"
|
27
|
-
|
28
|
-
sorted_keys = sort_any data.keys
|
29
|
-
|
30
|
-
data_values =
|
31
|
-
sorted_keys.map do |key|
|
32
|
-
value = data[key]
|
33
|
-
pad = " " * indent
|
34
|
-
subdata = ordered_data_string value, struct_only, indent + 1
|
35
|
-
"#{pad}#{key.inspect} => #{subdata}"
|
36
|
-
end
|
37
|
-
|
38
|
-
output << data_values.join(",\n") << "\n" unless data_values.empty?
|
39
|
-
output << "#{" " * indent}}"
|
40
|
-
|
41
|
-
when Array
|
42
|
-
output = "[\n"
|
43
|
-
|
44
|
-
data_values =
|
45
|
-
data.map do |value|
|
46
|
-
pad = " " * indent
|
47
|
-
"#{pad}#{ordered_data_string value, struct_only, indent + 1}"
|
48
|
-
end
|
49
|
-
|
50
|
-
output << data_values.join(",\n") << "\n" unless data_values.empty?
|
51
|
-
output << "#{" " * indent}]"
|
52
|
-
|
22
|
+
def self.ordered_data_string data, struct_only=false
|
23
|
+
case Kronk.config[:render_lang].to_s
|
24
|
+
when 'ruby' then DataRenderer.ruby(data, struct_only)
|
53
25
|
else
|
54
|
-
|
55
|
-
return "Boolean" if data == true || data == false
|
56
|
-
data.class
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
|
61
|
-
##
|
62
|
-
# Sorts an array of any combination of string, integer, or symbols.
|
63
|
-
|
64
|
-
def self.sort_any arr
|
65
|
-
i = 1
|
66
|
-
until i >= arr.length
|
67
|
-
j = i-1
|
68
|
-
val = arr[i]
|
69
|
-
prev_val = arr[j]
|
70
|
-
|
71
|
-
loop do
|
72
|
-
if smaller?(val, arr[j])
|
73
|
-
arr[j+1] = arr[j]
|
74
|
-
j = j - 1
|
75
|
-
break if j < 0
|
76
|
-
|
77
|
-
else
|
78
|
-
break
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
arr[j+1] = val
|
83
|
-
|
84
|
-
i = i.next
|
85
|
-
end
|
86
|
-
|
87
|
-
arr
|
88
|
-
end
|
89
|
-
|
90
|
-
|
91
|
-
##
|
92
|
-
# Compares Numerics, Strings, and Symbols and returns true if the left
|
93
|
-
# side is 'smaller' than the right side.
|
94
|
-
|
95
|
-
def self.smaller? left, right
|
96
|
-
case left
|
97
|
-
when Numeric
|
98
|
-
case right
|
99
|
-
when Numeric then right > left
|
100
|
-
else true
|
101
|
-
end
|
102
|
-
|
103
|
-
when Symbol
|
104
|
-
case right
|
105
|
-
when Numeric then false
|
106
|
-
when Symbol then right.to_s > left.to_s
|
107
|
-
else true
|
108
|
-
end
|
109
|
-
|
110
|
-
when String
|
111
|
-
case right
|
112
|
-
when String then right > left
|
113
|
-
else false
|
114
|
-
end
|
26
|
+
DataRenderer.json(data, struct_only)
|
115
27
|
end
|
116
28
|
end
|
117
29
|
|
@@ -94,6 +94,7 @@ class Kronk::Path::Transaction
|
|
94
94
|
|
95
95
|
|
96
96
|
def transaction_select data, *data_paths # :nodoc:
|
97
|
+
data_paths = data_paths.compact
|
97
98
|
return data if data_paths.empty?
|
98
99
|
|
99
100
|
new_data = Hash.new
|
@@ -129,6 +130,7 @@ class Kronk::Path::Transaction
|
|
129
130
|
|
130
131
|
|
131
132
|
def transaction_delete data, *data_paths # :nodoc:
|
133
|
+
data_paths = data_paths.compact
|
132
134
|
return data if data_paths.empty?
|
133
135
|
|
134
136
|
new_data = data.dup
|
data/lib/kronk/player.rb
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
class Kronk
|
2
|
+
|
3
|
+
##
|
4
|
+
# The Player class is used for running multiple requests and comparisons and
|
5
|
+
# providing useful output through Player::Output classes.
|
6
|
+
# Kronk includes a Suite (test-like) output, a Stream (chunked) output,
|
7
|
+
# and a Benchmark output.
|
8
|
+
|
9
|
+
class Player
|
10
|
+
|
11
|
+
attr_accessor :number, :concurrency, :queue, :count, :input,
|
12
|
+
:output, :mutex, :threads, :reader_thread
|
13
|
+
|
14
|
+
##
|
15
|
+
# Create a new Player for batch diff or response validation.
|
16
|
+
# Supported options are:
|
17
|
+
# :concurrency:: Fixnum - The maximum number of concurrent requests to make
|
18
|
+
# :number:: Fixnum - The number of requests to make
|
19
|
+
# :io:: IO - The IO instance to read from
|
20
|
+
# :output:: Class - The output class to use (see Player::Output)
|
21
|
+
# :parser:: Class - The IO parser to use.
|
22
|
+
|
23
|
+
def initialize opts={}
|
24
|
+
@number = opts[:number]
|
25
|
+
@concurrency = opts[:concurrency]
|
26
|
+
@concurrency = 1 if !@concurrency || @concurrency <= 0
|
27
|
+
self.output_from opts[:output] || Suite
|
28
|
+
|
29
|
+
@count = 0
|
30
|
+
@queue = []
|
31
|
+
@threads = []
|
32
|
+
@input = InputReader.new opts[:io], opts[:parser]
|
33
|
+
@last_req = nil
|
34
|
+
|
35
|
+
@mutex = Mutex.new
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
##
|
40
|
+
# The kind of output to use. Typically Player::Suite or Player::Stream.
|
41
|
+
# Takes an output class or a string that represents a class constant.
|
42
|
+
|
43
|
+
def output_from new_output
|
44
|
+
return @output = new_output.new(self) if Class === new_output
|
45
|
+
|
46
|
+
klass =
|
47
|
+
case new_output.to_s
|
48
|
+
when /^(Player::)?benchmark$/i then Benchmark
|
49
|
+
when /^(Player::)?stream$/i then Stream
|
50
|
+
when /^(Player::)?suite$/i then Suite
|
51
|
+
else
|
52
|
+
Kronk.find_const new_output
|
53
|
+
end
|
54
|
+
|
55
|
+
@output = klass.new self if klass
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
##
|
60
|
+
# Adds kronk request hash options to queue.
|
61
|
+
# See Kronk#compare for supported options.
|
62
|
+
|
63
|
+
def queue_req kronk_opts
|
64
|
+
@queue << kronk_opts
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
##
|
69
|
+
# Populate the queue by reading from the given IO instance and
|
70
|
+
# parsing it into kronk options.
|
71
|
+
#
|
72
|
+
# Default parser is RequestParser. See InputReader for parser requirements.
|
73
|
+
|
74
|
+
def from_io io, parser=nil
|
75
|
+
@input.io = io
|
76
|
+
@input.parser = parser if parser
|
77
|
+
@input
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
##
|
82
|
+
# Process the queue to compare two uris.
|
83
|
+
# If options are given, they are merged into every request.
|
84
|
+
|
85
|
+
def compare uri1, uri2, opts={}
|
86
|
+
return Cmd.compare uri1, uri2, @queue.shift.merge(opts) if single_request?
|
87
|
+
|
88
|
+
process_queue do |kronk_opts|
|
89
|
+
process_compare uri1, uri2, kronk_opts.merge(opts)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
##
|
95
|
+
# Process the queue to request uris.
|
96
|
+
# If options are given, they are merged into every request.
|
97
|
+
|
98
|
+
def request uri, opts={}
|
99
|
+
return Cmd.request(uri, @queue.shift.merge(opts)) if single_request?
|
100
|
+
|
101
|
+
process_queue do |kronk_opts|
|
102
|
+
process_request uri, kronk_opts.merge(opts)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
##
|
108
|
+
# Check if we're only processing a single case.
|
109
|
+
# If so, yield a single item and return immediately.
|
110
|
+
|
111
|
+
def single_request?
|
112
|
+
@queue << next_request if @queue.empty? && (!@number || @number <= 1)
|
113
|
+
@queue.length == 1 && @input.eof?
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
##
|
118
|
+
# Start processing the queue and reading from IO if available.
|
119
|
+
# Calls Output#start method and returns the value of Output#completed
|
120
|
+
# once processing is finished.
|
121
|
+
#
|
122
|
+
# Yields queue item until queue and io (if available) are empty and the
|
123
|
+
# totaly number of requests to run is met (if number is set).
|
124
|
+
|
125
|
+
def process_queue
|
126
|
+
@reader_thread = try_fill_queue
|
127
|
+
|
128
|
+
trap 'INT' do
|
129
|
+
@threads.each{|t| t.kill}
|
130
|
+
@threads.clear
|
131
|
+
@reader_thread.kill
|
132
|
+
output_results
|
133
|
+
exit 2
|
134
|
+
end
|
135
|
+
|
136
|
+
@output.start
|
137
|
+
@count = 0
|
138
|
+
|
139
|
+
until finished?
|
140
|
+
@threads.delete_if{|t| !t.alive? }
|
141
|
+
next if @threads.length >= @concurrency || @queue.empty?
|
142
|
+
|
143
|
+
@threads << Thread.new(@queue.shift) do |kronk_opts|
|
144
|
+
yield kronk_opts if block_given?
|
145
|
+
end
|
146
|
+
|
147
|
+
@count += 1
|
148
|
+
end
|
149
|
+
|
150
|
+
@threads.each{|t| t.join}
|
151
|
+
@threads.clear
|
152
|
+
|
153
|
+
@reader_thread.kill
|
154
|
+
|
155
|
+
output_results
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
##
|
160
|
+
# Attempt to fill the queue by reading from the IO instance.
|
161
|
+
# Starts a new thread and returns the thread instance.
|
162
|
+
|
163
|
+
def try_fill_queue
|
164
|
+
Thread.new do
|
165
|
+
loop do
|
166
|
+
break if !@number && @input.eof?
|
167
|
+
next if @queue.length >= @concurrency * 2
|
168
|
+
|
169
|
+
max_new = @concurrency * 2 - @queue.length
|
170
|
+
|
171
|
+
max_new.times do
|
172
|
+
@queue << next_request
|
173
|
+
break if !@number && @input.eof?
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
##
|
181
|
+
# Gets the next request to perform and always returns a Hash.
|
182
|
+
# Tries from input first, then from the last item in the queue.
|
183
|
+
# If both fail, returns an empty Hash.
|
184
|
+
|
185
|
+
def next_request
|
186
|
+
@last_req = @input.get_next || @queue.last || @last_req || Hash.new
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
##
|
191
|
+
# Returns true if processing queue should be stopped, otherwise false.
|
192
|
+
|
193
|
+
def finished?
|
194
|
+
(@number && @count >= @number) || @queue.empty? &&
|
195
|
+
@input.eof? && @count > 0 && !@reader_thread.alive?
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
##
|
200
|
+
# Process and output the results.
|
201
|
+
# Calls Output#completed method.
|
202
|
+
|
203
|
+
def output_results
|
204
|
+
@output.completed
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
##
|
209
|
+
# Run a single compare and call the Output#result or Output#error method.
|
210
|
+
|
211
|
+
def process_compare uri1, uri2, opts={}
|
212
|
+
kronk = Kronk.new opts
|
213
|
+
kronk.compare uri1, uri2
|
214
|
+
@output.result kronk, @mutex
|
215
|
+
|
216
|
+
rescue Kronk::Exception, Response::MissingParser, Errno::ECONNRESET => e
|
217
|
+
@output.error e, kronk, @mutex
|
218
|
+
end
|
219
|
+
|
220
|
+
|
221
|
+
##
|
222
|
+
# Run a single request and call the Output#result or Output#error method.
|
223
|
+
|
224
|
+
def process_request uri, opts={}
|
225
|
+
kronk = Kronk.new opts
|
226
|
+
kronk.retrieve uri
|
227
|
+
@output.result kronk, @mutex
|
228
|
+
|
229
|
+
rescue Kronk::Exception, Response::MissingParser, Errno::ECONNRESET => e
|
230
|
+
@output.error e, kronk, @mutex
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|