kronk 1.4.0 → 1.5.0
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/.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
|