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 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