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 ADDED
File without changes
data/History.rdoc CHANGED
@@ -1,3 +1,25 @@
1
+ === 1.5.0 / 2011-08-11
2
+
3
+ * Major Enhancements:
4
+
5
+ * JSON is the primary output format (instead of Ruby).
6
+
7
+ * Support for replay functionality from log files.
8
+
9
+ * Support for Suite, Stream, and Benchmark outputs.
10
+
11
+ * A great deal of Kronk, Cmd, Request and Response class refactoring.
12
+
13
+ * Plist and Nokogiri dependencies optional, based on usage.
14
+
15
+ * Enhancements:
16
+
17
+ * Configurable output indentation.
18
+
19
+ * Bugfixes:
20
+
21
+ * Custom config files would never load.
22
+
1
23
  === 1.4.0 / 2011-07-04
2
24
 
3
25
  * Enhancements:
data/Manifest.txt CHANGED
@@ -4,15 +4,22 @@ Manifest.txt
4
4
  README.rdoc
5
5
  Rakefile
6
6
  bin/kronk
7
- bin/yzma
8
7
  lib/kronk.rb
8
+ lib/kronk/constants.rb
9
9
  lib/kronk/cmd.rb
10
- lib/kronk/data_set.rb
10
+ lib/kronk/data_renderer.rb
11
11
  lib/kronk/diff.rb
12
12
  lib/kronk/diff/ascii_format.rb
13
13
  lib/kronk/diff/color_format.rb
14
14
  lib/kronk/path.rb
15
15
  lib/kronk/path/transaction.rb
16
+ lib/kronk/player.rb
17
+ lib/kronk/player/benchmark.rb
18
+ lib/kronk/player/input_reader.rb
19
+ lib/kronk/player/output.rb
20
+ lib/kronk/player/request_parser.rb
21
+ lib/kronk/player/suite.rb
22
+ lib/kronk/player/stream.rb
16
23
  lib/kronk/plist_parser.rb
17
24
  lib/kronk/request.rb
18
25
  lib/kronk/response.rb
@@ -21,9 +28,6 @@ lib/kronk/test/assertions.rb
21
28
  lib/kronk/test/core_ext.rb
22
29
  lib/kronk/test/helper_methods.rb
23
30
  lib/kronk/xml_parser.rb
24
- lib/yzma.rb
25
- lib/yzma/randomizer.rb
26
- lib/yzma/report.rb
27
31
  script/kronk_completion
28
32
  test/mocks/200_response.json
29
33
  test/mocks/200_response.plist
@@ -31,15 +35,20 @@ test/mocks/200_response.txt
31
35
  test/mocks/200_response.xml
32
36
  test/mocks/301_response.txt
33
37
  test/mocks/302_response.txt
38
+ test/mocks/cookies.yml
39
+ test/mocks/get_request.txt
34
40
  test/test_assertions.rb
35
41
  test/test_core_ext.rb
42
+ test/test_cmd.rb
36
43
  test/test_helper_methods.rb
37
- test/test_data_set.rb
38
44
  test/test_diff.rb
39
45
  test/test_helper.rb
46
+ test/test_input_reader.rb
40
47
  test/test_kronk.rb
41
48
  test/test_path.rb
49
+ test/test_player.rb
42
50
  test/test_request.rb
51
+ test/test_request_parser.rb
43
52
  test/test_response.rb
44
53
  test/test_transaction.rb
45
54
  test/test_xml_parser.rb
data/README.rdoc CHANGED
@@ -29,7 +29,7 @@ Kronk was made possible by the sponsoring of AT&T Interactive.
29
29
 
30
30
  * Helper methods for test suites.
31
31
 
32
- * Auto-query reports with request randomization support.
32
+ * Query and logfile playback with custom output.
33
33
 
34
34
  * Yes, it works on Windows.
35
35
 
@@ -141,6 +141,10 @@ Assign a default host to use when none is specified:
141
141
 
142
142
  :default_host: http://localhost:3000
143
143
 
144
+ Set the number of spaces for indentation:
145
+
146
+ :indentation: 2
147
+
144
148
  === Bash Completion:
145
149
 
146
150
  Bash completion is available by sourcing the file returned by:
@@ -190,7 +194,6 @@ attribute, you can do so by appending "/.." to the path.
190
194
  {
191
195
  "data" => {
192
196
  "path2" => [
193
- nil,
194
197
  {
195
198
  "first_child" => "first value",
196
199
  "child" => "child value",
@@ -262,10 +265,6 @@ single quotes around some of your paths.
262
265
 
263
266
  == REQUIREMENTS:
264
267
 
265
- * nokogiri gem
266
-
267
- * plist gem
268
-
269
268
  * json gem
270
269
 
271
270
  * cookiejar gem
data/Rakefile CHANGED
@@ -22,12 +22,12 @@ Hoe.spec 'kronk' do
22
22
  self.history_file = "History.rdoc"
23
23
  self.extra_rdoc_files = FileList['*.rdoc']
24
24
 
25
- self.extra_deps << ['plist', '~>3.1.0']
26
- self.extra_deps << ['json', '~>1.5']
27
- self.extra_deps << ['nokogiri', '~>1.4']
28
- self.extra_deps << ['cookiejar', '~>0.3.0']
25
+ self.extra_deps << ['json', '~>1.5']
26
+ self.extra_deps << ['cookiejar', '~>0.3.0']
29
27
 
30
- self.extra_dev_deps << ['mocha', '~>0.9.10']
28
+ self.extra_dev_deps << ['plist', '~>3.1.0']
29
+ self.extra_dev_deps << ['nokogiri', '~>1.4']
30
+ self.extra_dev_deps << ['mocha', '~>0.9.12']
31
31
  end
32
32
 
33
33
 
data/lib/kronk.rb CHANGED
@@ -3,6 +3,10 @@ require 'rubygems'
3
3
  require 'json'
4
4
  require 'cookiejar'
5
5
 
6
+ require 'thread'
7
+ require 'stringio'
8
+ require 'base64'
9
+
6
10
  require 'net/https'
7
11
  require 'optparse'
8
12
  require 'yaml'
@@ -10,13 +14,20 @@ require 'yaml'
10
14
  class Kronk
11
15
 
12
16
  # This gem's version.
13
- VERSION = '1.4.0'
14
-
15
-
17
+ VERSION = '1.5.0'
18
+
19
+ require 'kronk/constants'
20
+ require 'kronk/player'
21
+ require 'kronk/player/output'
22
+ require 'kronk/player/suite'
23
+ require 'kronk/player/stream'
24
+ require 'kronk/player/benchmark'
25
+ require 'kronk/player/request_parser'
26
+ require 'kronk/player/input_reader'
16
27
  require 'kronk/cmd'
17
28
  require 'kronk/path'
18
29
  require 'kronk/path/transaction'
19
- require 'kronk/data_set'
30
+ require 'kronk/data_renderer'
20
31
  require 'kronk/diff/ascii_format'
21
32
  require 'kronk/diff/color_format'
22
33
  require 'kronk/diff'
@@ -26,74 +37,6 @@ class Kronk
26
37
  require 'kronk/xml_parser'
27
38
 
28
39
 
29
- # Config directory.
30
- CONFIG_DIR = File.expand_path "~/.kronk"
31
-
32
- # Default config file to load. Defaults to ~/.kronk.
33
- DEFAULT_CONFIG_FILE = File.join CONFIG_DIR, "rc"
34
-
35
- # Default cache file.
36
- DEFAULT_CACHE_FILE = File.join CONFIG_DIR, "cache"
37
-
38
- # Default cookies file.
39
- DEFAULT_COOKIES_FILE = File.join CONFIG_DIR, "cookies"
40
-
41
- # Default file with history of unique URIs. (Used for autocomplete)
42
- DEFAULT_HISTORY_FILE = File.join CONFIG_DIR, "history"
43
-
44
-
45
- # Default Content-Type header to parser mapping.
46
- DEFAULT_CONTENT_TYPES = {
47
- 'js' => 'JSON',
48
- 'json' => 'JSON',
49
- 'plist' => 'PlistParser',
50
- 'xml' => 'XMLParser'
51
- }
52
-
53
-
54
- # Aliases for various user-agents. Thanks Mechanize! :)
55
- USER_AGENTS = {
56
- 'kronk' =>
57
- "Kronk/#{VERSION} (http://github.com/yaksnrainbows/kronk)",
58
- 'iphone' =>
59
- "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1C28 Safari/419.3",
60
- 'linux_firefox' =>
61
- "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.1) Gecko/20100122 firefox/3.6.1",
62
- 'linux_mozilla' =>
63
- "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4) Gecko/20030624",
64
- 'mac_mozilla' =>
65
- "Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.4a) Gecko/20030401",
66
- 'linux_konqueror' =>
67
- "Mozilla/5.0 (compatible; Konqueror/3; Linux)",
68
- 'mac_firefox' =>
69
- "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6",
70
- 'mac_safari' =>
71
- "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_2; de-at) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10",
72
- 'win_ie6' =>
73
- "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
74
- 'win_ie7' =>
75
- "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
76
- 'win_mozilla' =>
77
- "Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.4b) Gecko/20030516 Mozilla Firebird/0.6"
78
- }
79
-
80
-
81
- # Default config to use.
82
- DEFAULT_CONFIG = {
83
- :content_types => DEFAULT_CONTENT_TYPES.dup,
84
- :cache_file => DEFAULT_CACHE_FILE,
85
- :cookies_file => DEFAULT_COOKIES_FILE,
86
- :default_host => "http://localhost:3000",
87
- :diff_format => :ascii_diff,
88
- :history_file => DEFAULT_HISTORY_FILE,
89
- :requires => [],
90
- :show_lines => false,
91
- :uri_options => {},
92
- :use_cookies => true,
93
- :user_agents => USER_AGENTS.dup
94
- }
95
-
96
-
97
40
  ##
98
41
  # Read the Kronk config hash.
99
42
 
@@ -106,107 +49,15 @@ class Kronk
106
49
  # Load a config file and apply to Kronk.config.
107
50
 
108
51
  def self.load_config filepath=DEFAULT_CONFIG_FILE
109
- conf = YAML.load_file DEFAULT_CONFIG_FILE
110
- content_types = conf.delete :content_types
111
- uri_options = conf.delete :uri_options
112
- user_agents = conf.delete :user_agents
113
-
114
- if conf[:requires]
115
- requires = [*conf.delete(:requires)]
116
- self.config[:requires] ||= []
117
- self.config[:requires].concat requires
118
- end
119
-
120
- self.config[:uri_options].merge! uri_options if uri_options
121
- self.config[:content_types].merge! content_types if content_types
122
- self.config[:user_agents].merge! user_agents if user_agents
123
-
124
- self.config.merge! conf
125
- end
126
-
127
-
128
- ##
129
- # Load the config-based requires.
130
-
131
- def self.load_requires more_requires=nil
132
- return unless config[:requires] || more_requires
133
- (config[:requires] | more_requires.to_a).each{|lib| require lib }
134
- end
135
-
136
-
137
- ##
138
- # Creates the default config file at the given path.
139
-
140
- def self.make_config_file
141
- Dir.mkdir CONFIG_DIR unless File.directory? CONFIG_DIR
142
-
143
- File.open DEFAULT_CONFIG_FILE, "w+" do |file|
144
- file << DEFAULT_CONFIG.to_yaml
145
- end
146
- end
147
-
148
-
149
- ##
150
- # Returns merged config-defined options for a given uri.
151
- # Values in cmd_opts take precedence.
152
- # Returns cmd_opts Hash if none found.
153
-
154
- def self.merge_options_for_uri uri, cmd_opts={}
155
- return cmd_opts if Kronk.config[:no_uri_options]
156
-
157
- out_opts = Hash.new.merge cmd_opts
158
-
159
- Kronk.config[:uri_options].each do |matcher, opts|
160
- next unless (uri == matcher || uri =~ %r{#{matcher}}) && Hash === opts
161
-
162
- opts.each do |key, val|
163
- if !out_opts[key]
164
- out_opts[key] = val
165
- next
166
- end
167
-
168
-
169
- case key
52
+ conf = YAML.load_file filepath
170
53
 
171
- # Hash or uri query String
172
- when :data, :query
173
- val = Request.parse_nested_query val if String === val
174
-
175
- out_opts[key] = Request.parse_nested_query out_opts[key] if
176
- String === out_opts[key]
177
-
178
- out_opts[key] = val.merge out_opts[key], &DataSet::DEEP_MERGE
179
-
180
- # Hashes
181
- when :headers, :auth
182
- out_opts[key] = val.merge out_opts[key]
54
+ self.config[:requires].concat [*conf.delete(:requires)] if conf[:requires]
183
55
 
184
- # Proxy hash or String
185
- when :proxy
186
- if Hash === val && Hash === out_opts[key]
187
- out_opts[key] = val.merge out_opts[key]
188
-
189
- elsif Hash === val && String === out_opts[key]
190
- val[:address] = out_opts[key]
191
- out_opts[key] = val
192
-
193
- elsif String === val && Hash === out_opts[key]
194
- out_opts[key][:address] ||= val
195
- end
196
-
197
- # Response headers - Boolean, String, or Array
198
- when :with_headers
199
- next if out_opts[key] == true || out_opts[key] && val == true
200
- out_opts[key] = [*out_opts[key]] | [*val]
201
-
202
- # String or Array
203
- when :only_data, :only_data_with, :ignore_data, :ignore_data_with
204
- out_opts[key] = [*out_opts[key]] | [*val]
205
- end
206
- end
56
+ [:content_types, :uri_options, :user_agents].each do |key|
57
+ self.config[key].merge! conf.delete(key) if conf[key]
207
58
  end
208
59
 
209
- out_opts
60
+ self.config.merge! conf
210
61
  end
211
62
 
212
63
 
@@ -306,9 +157,29 @@ class Kronk
306
157
 
307
158
 
308
159
  ##
309
- # Make requests, parse the responses and compare the data.
310
- # Query arguments may be set to the special value :cache to use the
311
- # last live http response retrieved.
160
+ # See Kronk#compare. Short for:
161
+ # Kronk.new(opts).compare(uri1, uri2)
162
+
163
+ def self.compare uri1, uri2, opts={}
164
+ new(opts).compare uri1, uri2
165
+ end
166
+
167
+
168
+ ##
169
+ # See Kronk#retrieve. Short for:
170
+ # Kronk.new(opts).retrieve(uri)
171
+
172
+ def self.retrieve uri, opts={}
173
+ new(opts).retrieve uri
174
+ end
175
+
176
+
177
+ attr_accessor :diff, :options, :response, :responses
178
+
179
+
180
+ ##
181
+ # Create a Kronk instance to keep references to all request, response,
182
+ # and diff data.
312
183
  #
313
184
  # Supports the following options:
314
185
  # :data:: Hash/String - the data to pass to the http request
@@ -321,51 +192,150 @@ class Kronk
321
192
  # :proxy:: Hash/String - http proxy to use; defaults to nil
322
193
  # :only_data:: String/Array - extracts the data from given data paths
323
194
  # :ignore_data:: String/Array - defines which data points to exclude
324
- # :keep_indicies:: Boolean - keep the original indicies of modified arrays,
325
- # and return them as hashes.
195
+ # :keep_indicies:: Boolean - indicies of modified arrays display as hashes.
326
196
  # :with_headers:: Boolean/String/Array - defines which headers to include
327
197
  # :parser:: Object/String - the parser to use for the body; default nil
328
198
  # :raw:: Boolean - run diff on raw strings
199
+
200
+ def initialize opts={}
201
+ @options = opts
202
+ @diff = nil
203
+ @responses = []
204
+ @response = nil
205
+ end
206
+
207
+
208
+ ##
209
+ # Make requests, parse the responses and compare the data.
210
+ # Query arguments may be set to the special value :cache to use the
211
+ # last live http response retrieved.
329
212
  #
330
213
  # Returns a diff object.
331
214
 
332
- def self.compare uri1, uri2, options={}
215
+ def compare uri1, uri2
333
216
  str1 = str2 = ""
217
+ res1 = res2 = nil
218
+
219
+ t1 = Thread.new do
220
+ res1 = retrieve uri1
221
+ str1 = res1.stringify @options
222
+ end
334
223
 
335
- t1 = Thread.new{ str1 = retrieve_data_string uri1, options }
336
- t2 = Thread.new{ str2 = retrieve_data_string uri2, options }
224
+ t2 = Thread.new do
225
+ res2 = retrieve uri2
226
+ str2 = res2.stringify @options
227
+ end
337
228
 
338
229
  t1.join
339
230
  t2.join
340
231
 
341
- Diff.new str1, str2
232
+ @responses = [res1, res2]
233
+ @response = res2
234
+
235
+ @diff = Diff.new str1, str2
342
236
  end
343
237
 
344
238
 
345
239
  ##
346
- # Return a data string, parsed or raw.
347
- # See Kronk.compare for supported options.
240
+ # Returns a Response instance from a url, file, or IO as a String.
348
241
 
349
- def self.retrieve_data_string uri, options={}
350
- options = merge_options_for_uri uri, options
242
+ def retrieve uri
243
+ options = Kronk.config[:no_uri_options] ? options_for_uri(uri) : @options
351
244
 
352
- resp = Request.retrieve uri, options
245
+ if IO === uri || StringIO === uri
246
+ Cmd.verbose "Reading IO #{uri}"
247
+ resp = Response.new uri
353
248
 
354
- if options[:irb]
355
- Cmd.irb resp
356
-
357
- elsif options[:raw]
358
- resp.selective_string options
249
+ elsif File.file? uri.to_s
250
+ Cmd.verbose "Reading file: #{uri}\n"
251
+ resp = Response.read_file uri
359
252
 
360
253
  else
361
- begin
362
- data = resp.selective_data options
363
- Diff.ordered_data_string data, options[:struct]
254
+ req = Request.new uri, options
255
+ Cmd.verbose "Retrieving URL: #{req.uri}\n"
256
+ resp = req.retrieve
257
+ Kronk.history << uri
258
+ end
364
259
 
365
- rescue Response::MissingParser
366
- Cmd.verbose "Warning: No parser for #{resp['Content-Type']} [#{uri}]"
367
- resp.selective_string options
260
+ max_rdir = options[:follow_redirects]
261
+ while resp.redirect? && (max_rdir == true || max_rdir.to_s.to_i > 0)
262
+ Cmd.verbose "Following redirect..."
263
+ resp = resp.follow_redirect
264
+ max_rdir = max_rdir - 1 if Fixnum === max_rdir
265
+ end
266
+
267
+ @responses = [resp]
268
+ @response = resp
269
+ @diff = nil
270
+
271
+ resp
272
+
273
+ rescue SocketError, Errno::ENOENT, Errno::ECONNREFUSED
274
+ raise NotFoundError, "#{uri} could not be found"
275
+
276
+ rescue Timeout::Error
277
+ raise TimeoutError, "#{uri} took too long to respond"
278
+ end
279
+
280
+
281
+ ##
282
+ # Returns merged config-defined options for a given uri.
283
+ # Values in cmd_opts take precedence.
284
+ # Returns cmd_opts Hash if none found.
285
+
286
+ def options_for_uri uri
287
+ out_opts = @options.dup
288
+
289
+ Kronk.config[:uri_options].each do |matcher, opts|
290
+ next unless (uri == matcher || uri =~ %r{#{matcher}}) && Hash === opts
291
+
292
+ opts.each do |key, val|
293
+ if out_opts[key].nil?
294
+ out_opts[key] = val
295
+ next
296
+ end
297
+
298
+
299
+ case key
300
+
301
+ # Hash or uri query String
302
+ when :data, :query
303
+ val = Request.parse_nested_query val if String === val
304
+
305
+ out_opts[key] = Request.parse_nested_query out_opts[key] if
306
+ String === out_opts[key]
307
+
308
+ out_opts[key] = val.merge out_opts[key], &DEEP_MERGE
309
+
310
+ # Hashes
311
+ when :headers, :auth
312
+ out_opts[key] = val.merge out_opts[key]
313
+
314
+ # Proxy hash or String
315
+ when :proxy
316
+ if Hash === val && Hash === out_opts[key]
317
+ out_opts[key] = val.merge out_opts[key]
318
+
319
+ elsif Hash === val && String === out_opts[key]
320
+ val[:address] = out_opts[key]
321
+ out_opts[key] = val
322
+
323
+ elsif String === val && Hash === out_opts[key]
324
+ out_opts[key][:address] ||= val
325
+ end
326
+
327
+ # Response headers - Boolean, String, or Array
328
+ when :with_headers
329
+ next if out_opts[key] == true || out_opts[key] && val == true
330
+ out_opts[key] = [*out_opts[key]] | [*val]
331
+
332
+ # String or Array
333
+ when :only_data, :ignore_data
334
+ out_opts[key] = [*out_opts[key]] | [*val]
335
+ end
368
336
  end
369
337
  end
338
+
339
+ out_opts
370
340
  end
371
341
  end