kronk 1.2.5 → 1.3.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 +30 -2
- data/Manifest.txt +14 -0
- data/README.rdoc +5 -7
- data/Rakefile +88 -4
- data/bin/kronk +2 -1
- data/bin/yzma +13 -0
- data/lib/kronk.rb +112 -430
- data/lib/kronk/cmd.rb +469 -0
- data/lib/kronk/data_set.rb +38 -44
- data/lib/kronk/diff.rb +105 -112
- data/lib/kronk/diff/ascii_format.rb +35 -0
- data/lib/kronk/diff/color_format.rb +49 -0
- data/lib/kronk/request.rb +6 -6
- data/lib/kronk/test.rb +15 -0
- data/lib/kronk/test/assertions.rb +97 -0
- data/lib/kronk/test/core_ext.rb +65 -0
- data/lib/kronk/test/helper_methods.rb +86 -0
- data/lib/yzma.rb +174 -0
- data/lib/yzma/randomizer.rb +54 -0
- data/lib/yzma/report.rb +47 -0
- data/test/test_assertions.rb +93 -0
- data/test/test_core_ext.rb +74 -0
- data/test/test_data_set.rb +41 -32
- data/test/test_diff.rb +50 -24
- data/test/test_helper_methods.rb +177 -0
- data/test/test_kronk.rb +3 -1
- metadata +36 -19
@@ -0,0 +1,49 @@
|
|
1
|
+
class Kronk
|
2
|
+
|
3
|
+
class Diff
|
4
|
+
|
5
|
+
##
|
6
|
+
# Format diff with ascii
|
7
|
+
|
8
|
+
class ColorFormat
|
9
|
+
|
10
|
+
def self.ensure_color
|
11
|
+
return unless Kronk::Cmd.windows?
|
12
|
+
begin
|
13
|
+
require 'Win32/Console/ANSI'
|
14
|
+
rescue LoadError
|
15
|
+
Cmd.warn "You must gem install win32console to use color"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def self.lines line_nums, col_width
|
21
|
+
ensure_color
|
22
|
+
|
23
|
+
out =
|
24
|
+
[*line_nums].map do |lnum|
|
25
|
+
lnum.to_s.rjust col_width
|
26
|
+
end.join "\033[32m"
|
27
|
+
|
28
|
+
"\033[7;31m#{out}\033[0m "
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def self.deleted str
|
33
|
+
ensure_color
|
34
|
+
"\033[31m- #{str}\033[0m"
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def self.added str
|
39
|
+
ensure_color
|
40
|
+
"\033[32m+ #{str}\033[0m"
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def self.common str
|
45
|
+
" #{str}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/kronk/request.rb
CHANGED
@@ -19,7 +19,7 @@ class Kronk
|
|
19
19
|
# number of redirects left if it's an Integer.
|
20
20
|
|
21
21
|
def self.follow_redirect resp, options={}
|
22
|
-
Kronk.verbose "Following redirect..."
|
22
|
+
Kronk::Cmd.verbose "Following redirect..."
|
23
23
|
|
24
24
|
rdir = options[:follow_redirects]
|
25
25
|
rdir = rdir - 1 if Integer === rdir && rdir > 0
|
@@ -84,7 +84,7 @@ class Kronk
|
|
84
84
|
# Read http response from a file and return a HTTPResponse instance.
|
85
85
|
|
86
86
|
def self.retrieve_file path, options={}
|
87
|
-
Kronk.verbose "Reading file: #{path}\n"
|
87
|
+
Kronk::Cmd.verbose "Reading file: #{path}\n"
|
88
88
|
|
89
89
|
options = options.dup
|
90
90
|
|
@@ -95,7 +95,7 @@ class Kronk
|
|
95
95
|
|
96
96
|
# On windows, read the full file and insert contents into
|
97
97
|
# a StringIO to avoid failures with IO#read_nonblock
|
98
|
-
file = StringIO.new file.read if Kronk.windows?
|
98
|
+
file = StringIO.new file.read if Kronk::Cmd.windows?
|
99
99
|
|
100
100
|
begin
|
101
101
|
resp = Response.read_new file
|
@@ -117,7 +117,7 @@ class Kronk
|
|
117
117
|
# Read the http response from an IO instance and return a HTTPResponse.
|
118
118
|
|
119
119
|
def self.retrieve_io io, options={}
|
120
|
-
Kronk.verbose "Reading IO..."
|
120
|
+
Kronk::Cmd.verbose "Reading IO..."
|
121
121
|
|
122
122
|
options = options.dup
|
123
123
|
|
@@ -221,7 +221,7 @@ class Kronk
|
|
221
221
|
options[:auth][:password]
|
222
222
|
end
|
223
223
|
|
224
|
-
Kronk.verbose "Retrieving URL: #{uri}\n"
|
224
|
+
Kronk::Cmd.verbose "Retrieving URL: #{uri}\n"
|
225
225
|
|
226
226
|
http.request req, data
|
227
227
|
end
|
@@ -289,7 +289,7 @@ class Kronk
|
|
289
289
|
user = proxy_opts[:username]
|
290
290
|
pass = proxy_opts[:password]
|
291
291
|
|
292
|
-
Kronk.verbose "Using proxy #{addr}\n" if host
|
292
|
+
Kronk::Cmd.verbose "Using proxy #{addr}\n" if host
|
293
293
|
|
294
294
|
Net::HTTP::Proxy host, port, user, pass
|
295
295
|
end
|
data/lib/kronk/test.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
class Kronk
|
2
|
+
|
3
|
+
##
|
4
|
+
# Test module that includes kronk assertions, request helper methods,
|
5
|
+
# and core extensions.
|
6
|
+
|
7
|
+
module Test
|
8
|
+
require 'kronk/test/assertions'
|
9
|
+
require 'kronk/test/core_ext'
|
10
|
+
require 'kronk/test/helper_methods'
|
11
|
+
|
12
|
+
include Assertions
|
13
|
+
include HelperMethods
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
class Kronk
|
2
|
+
|
3
|
+
module Test
|
4
|
+
|
5
|
+
module Assertions
|
6
|
+
|
7
|
+
##
|
8
|
+
# Assert that the given path exists in data.
|
9
|
+
# Supports all DataSet#find_data path types.
|
10
|
+
|
11
|
+
def assert_data_at data, path, msg=nil
|
12
|
+
msg ||= "No data found at #{path.inspect} for #{data.inspect}"
|
13
|
+
found = false
|
14
|
+
|
15
|
+
data_set = Kronk::DataSet.new data
|
16
|
+
data_set.find_data path do |d,k,p|
|
17
|
+
found = true
|
18
|
+
break
|
19
|
+
end
|
20
|
+
|
21
|
+
assert found, msg
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
##
|
26
|
+
# Assert that the given path doesn't exist in data.
|
27
|
+
# Supports all DataSet#find_data path types.
|
28
|
+
|
29
|
+
def assert_no_data_at data, path, msg=nil
|
30
|
+
msg ||= "Data found at #{path.inspect} for #{data.inspect}"
|
31
|
+
found = false
|
32
|
+
|
33
|
+
data_set = Kronk::DataSet.new data
|
34
|
+
data_set.find_data path do |d,k,p|
|
35
|
+
found = true
|
36
|
+
break
|
37
|
+
end
|
38
|
+
|
39
|
+
assert !found, msg
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
##
|
44
|
+
# Assert that at least one data point found with the given path is equal
|
45
|
+
# to the given match.
|
46
|
+
# Supports all DataSet#find_data path types.
|
47
|
+
|
48
|
+
def assert_data_at_equal data, path, match, msg=nil
|
49
|
+
last_data = nil
|
50
|
+
found = false
|
51
|
+
|
52
|
+
data_set = Kronk::DataSet.new data
|
53
|
+
data_set.find_data path do |d,k,p|
|
54
|
+
found = true
|
55
|
+
last_data = d[k]
|
56
|
+
break if d[k] == match
|
57
|
+
end
|
58
|
+
|
59
|
+
assert found,
|
60
|
+
msg || "No data found at #{path.inspect} for #{data.inspect}"
|
61
|
+
|
62
|
+
assert_equal match, last_data, msg
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
##
|
67
|
+
# Assert that no data points found with the given path are equal
|
68
|
+
# to the given match.
|
69
|
+
# Supports all DataSet#find_data path types.
|
70
|
+
|
71
|
+
def assert_data_at_not_equal data, path, match, msg=nil
|
72
|
+
last_data = nil
|
73
|
+
|
74
|
+
data_set = Kronk::DataSet.new data
|
75
|
+
data_set.find_data path do |d,k,p|
|
76
|
+
last_data = d[k]
|
77
|
+
break if d[k] == match
|
78
|
+
end
|
79
|
+
|
80
|
+
assert_not_equal match, last_data, msg
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
##
|
85
|
+
# Makes request to both uris and asserts that the parsed data they
|
86
|
+
# return is equal. Compares response body if data is unparsable.
|
87
|
+
# Supports all options of Kronk.compare.
|
88
|
+
|
89
|
+
def assert_equal_responses uri1, uri2, options={}
|
90
|
+
resp1 = Kronk.retrieve_data_string uri1, options
|
91
|
+
resp2 = Kronk.retrieve_data_string uri2, options
|
92
|
+
|
93
|
+
assert_equal resp1, resp2
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class Kronk
|
2
|
+
|
3
|
+
module Test
|
4
|
+
|
5
|
+
##
|
6
|
+
# Data manipulation and retrieval methods for Array and Hash classes.
|
7
|
+
|
8
|
+
module DataExt
|
9
|
+
|
10
|
+
##
|
11
|
+
# Checks if the given path exists and returns the first matching path
|
12
|
+
# as an array of keys. Returns nil if no path is found.
|
13
|
+
|
14
|
+
def has_path? path
|
15
|
+
Kronk::DataSet.new(self).find_data path do |d,k,p|
|
16
|
+
return !!p
|
17
|
+
end
|
18
|
+
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
##
|
24
|
+
# Looks for data at paths matching path. Returns a hash of
|
25
|
+
# path array => data value pairs.
|
26
|
+
#
|
27
|
+
# If given a block will pass the parent data structure, the key
|
28
|
+
# or index of the item at given path, and the full path
|
29
|
+
# as an array of keys for each found path.
|
30
|
+
#
|
31
|
+
# data = {:foo => "bar", :foobar => [:a, :b, {:foo => "other bar"}, :c]}
|
32
|
+
# data.find_data "**/foo" do |parent, key, path|
|
33
|
+
# p path
|
34
|
+
# p parent[key]
|
35
|
+
# puts "---"
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# # outputs:
|
39
|
+
# # [:foo]
|
40
|
+
# # "bar"
|
41
|
+
# # ---
|
42
|
+
# # [:foobar, 2, :foo]
|
43
|
+
# # "other bar"
|
44
|
+
# # ---
|
45
|
+
#
|
46
|
+
# # returns:
|
47
|
+
# # {[:foo] => "bar", [:foobar, 2, :foo] => "other bar"}
|
48
|
+
|
49
|
+
def find_data path
|
50
|
+
found = {}
|
51
|
+
|
52
|
+
Kronk::DataSet.new(self).find_data path do |d,k,p|
|
53
|
+
found[p] = d[k]
|
54
|
+
yield d, k, p if block_given?
|
55
|
+
end
|
56
|
+
|
57
|
+
found
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
Array.send :include, Kronk::Test::DataExt
|
65
|
+
Hash.send :include, Kronk::Test::DataExt
|
@@ -0,0 +1,86 @@
|
|
1
|
+
class Kronk
|
2
|
+
|
3
|
+
module Test
|
4
|
+
|
5
|
+
##
|
6
|
+
# Kronk test helper methods to easily make and mock requests.
|
7
|
+
# Sets @responses, @response, @datas, @data, and @diff instance variables.
|
8
|
+
|
9
|
+
module HelperMethods
|
10
|
+
|
11
|
+
##
|
12
|
+
# Do a get request for one or two URIs.
|
13
|
+
# See Kronk#compare for all supported options.
|
14
|
+
|
15
|
+
def get uri1, uri2=nil, options={}
|
16
|
+
retrieve uri1, uri2, options.merge(:http_method => :get)
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
##
|
21
|
+
# Do a post request for one or two URIs.
|
22
|
+
# See Kronk#compare for all supported options.
|
23
|
+
|
24
|
+
def post uri1, uri2=nil, options={}
|
25
|
+
retrieve uri1, uri2, options.merge(:http_method => :post)
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
##
|
30
|
+
# Do a put request for one or two URIs.
|
31
|
+
# See Kronk#compare for all supported options.
|
32
|
+
|
33
|
+
def put uri1, uri2=nil, options={}
|
34
|
+
retrieve uri1, uri2, options.merge(:http_method => :put)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
##
|
39
|
+
# Do a delete request for one or two URIs.
|
40
|
+
# See Kronk#compare for all supported options.
|
41
|
+
|
42
|
+
def delete uri1, uri2=nil, options={}
|
43
|
+
retrieve uri1, uri2, options.merge(:http_method => :delete)
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def retrieve uri1, uri2=nil, options={}
|
50
|
+
uri2, options = nil, uri2.merge(options) if Hash === uri2
|
51
|
+
|
52
|
+
if uri2
|
53
|
+
@responses = [Request.retrieve(uri1, options),
|
54
|
+
Request.retrieve(uri2, options)]
|
55
|
+
@response = @responses.last
|
56
|
+
|
57
|
+
@datas = @responses.map do |r|
|
58
|
+
begin
|
59
|
+
r.selective_data options
|
60
|
+
rescue Kronk::Response::MissingParser
|
61
|
+
r.body
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
@data = @datas.last
|
66
|
+
|
67
|
+
@diff = Diff.new_from_data(*@datas)
|
68
|
+
|
69
|
+
else
|
70
|
+
@response = Request.retrieve uri1, options
|
71
|
+
@responses = [@response]
|
72
|
+
|
73
|
+
@data = begin
|
74
|
+
@response.selective_data options
|
75
|
+
rescue Kronk::Response::MissingParser
|
76
|
+
@response.body
|
77
|
+
end
|
78
|
+
|
79
|
+
@datas = [@data]
|
80
|
+
|
81
|
+
@diff = nil
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/yzma.rb
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'kronk'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Yzma bosses Kronk around to give you meaningful diff variation
|
6
|
+
# statistical data:
|
7
|
+
# Yzma.report "My Report" do
|
8
|
+
# compare uri1, uri2, :count => 100 do
|
9
|
+
# randomize_param :limit, 20..200, :optional => true, :allow_blank => true
|
10
|
+
#
|
11
|
+
# randomize_param :q, %w{pizza restaurant flowers}
|
12
|
+
#
|
13
|
+
# randomize_param :g, %w{91106 91203}
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# compare uri3, uri4, :count => 10,
|
17
|
+
# :title => "Second Compare"
|
18
|
+
# end
|
19
|
+
|
20
|
+
class Yzma
|
21
|
+
|
22
|
+
require 'yzma/report'
|
23
|
+
require 'yzma/randomizer'
|
24
|
+
|
25
|
+
|
26
|
+
##
|
27
|
+
# Parses ARGV for Yzma.
|
28
|
+
|
29
|
+
def self.parse_args argv
|
30
|
+
options = {:files => []}
|
31
|
+
|
32
|
+
opts = OptionParser.new do |opt|
|
33
|
+
opt.program_name = File.basename $0
|
34
|
+
opt.version = Kronk::VERSION
|
35
|
+
opt.release = nil
|
36
|
+
|
37
|
+
opt.banner = <<-STR
|
38
|
+
|
39
|
+
#{opt.program_name} #{opt.version}
|
40
|
+
|
41
|
+
Run diff reports between two URI requests.
|
42
|
+
|
43
|
+
Usage:
|
44
|
+
#{opt.program_name} --help
|
45
|
+
#{opt.program_name} --version
|
46
|
+
#{opt.program_name} file1 [file2 ...]
|
47
|
+
STR
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.parse! argv
|
51
|
+
|
52
|
+
options[:files] = argv.dup
|
53
|
+
|
54
|
+
if options[:files].empty?
|
55
|
+
$stderr << "\nError: At least one report file must be specified.\n"
|
56
|
+
$stderr << "See 'yzma --help' for usage\n\n"
|
57
|
+
exit 1
|
58
|
+
end
|
59
|
+
|
60
|
+
options
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
##
|
65
|
+
# Construct and run an Yzma report.
|
66
|
+
|
67
|
+
def self.report name_or_report=nil, &block
|
68
|
+
yzma = new name_or_report
|
69
|
+
|
70
|
+
yzma.instance_eval(&block)
|
71
|
+
|
72
|
+
yzma.report.header << "Ran #{yzma.comparisons} URI comparison(s)"
|
73
|
+
yzma.report.header << "Iterated a total of #{yzma.iterations} case(s)"
|
74
|
+
|
75
|
+
curr_req = nil
|
76
|
+
|
77
|
+
yzma.report.write do |req, data|
|
78
|
+
next unless data[:diff] > 0
|
79
|
+
"\n#{data[:diff]} avg diffs:\n#{req[0]} - #{req[1]}\n"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
##
|
85
|
+
# Run the Yzma command.
|
86
|
+
|
87
|
+
def self.run argv=ARGV
|
88
|
+
options = parse_args argv
|
89
|
+
options[:files].each do |file|
|
90
|
+
self.instance_eval File.read(file)
|
91
|
+
end
|
92
|
+
|
93
|
+
exit 2 if self.diffs > 0
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
class << self
|
98
|
+
attr_accessor :diffs
|
99
|
+
end
|
100
|
+
|
101
|
+
self.diffs = 0
|
102
|
+
|
103
|
+
|
104
|
+
attr_reader :report, :comparisons, :iterations
|
105
|
+
|
106
|
+
##
|
107
|
+
# Initialize Izma with optional name.
|
108
|
+
|
109
|
+
def initialize name_or_report=nil
|
110
|
+
case name_or_report
|
111
|
+
when String
|
112
|
+
@name = name_or_report
|
113
|
+
@report = Report.new @name
|
114
|
+
|
115
|
+
when Report
|
116
|
+
@report = name_or_report
|
117
|
+
@name = @report.name
|
118
|
+
|
119
|
+
else
|
120
|
+
@name = 'Yzma Report'
|
121
|
+
@report = Report.new @name
|
122
|
+
end
|
123
|
+
|
124
|
+
@comparisons = 0
|
125
|
+
@iterations = 0
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
##
|
130
|
+
# Compare two paths or uris. Second uri may be omitted.
|
131
|
+
# Supports all Kronk.compare options, plus:
|
132
|
+
# :count:: Integer - number of times to run the endpoint; default 1.
|
133
|
+
# :title:: String - title to display when running compares.
|
134
|
+
|
135
|
+
def compare uri1, uri2, options={}, &block
|
136
|
+
options = options.dup
|
137
|
+
count = options.delete(:count) || 1
|
138
|
+
title = options.delete(:title) || "#{uri1} --- #{uri2}"
|
139
|
+
|
140
|
+
@comparisons = @comparisons.next
|
141
|
+
|
142
|
+
diff_avg = 0
|
143
|
+
diff_cnt = 0
|
144
|
+
|
145
|
+
puts title
|
146
|
+
1.upto(count) do |i|
|
147
|
+
randomizer = Randomizer.new
|
148
|
+
randomizer.instance_eval &block if block_given?
|
149
|
+
|
150
|
+
randomized_opts = options.merge randomizer.to_options
|
151
|
+
|
152
|
+
begin
|
153
|
+
diff = Kronk.compare uri1, uri2, randomized_opts
|
154
|
+
|
155
|
+
diff_avg = (diff_avg * diff_cnt + diff.count) / (diff_cnt + 1)
|
156
|
+
diff_cnt = diff_cnt.next
|
157
|
+
|
158
|
+
@iterations = @iterations.next
|
159
|
+
|
160
|
+
$stdout << (diff.count > 0 ? "D" : ".")
|
161
|
+
self.class.diffs = self.class.diffs + diff.count
|
162
|
+
|
163
|
+
rescue Kronk::Request::NotFoundError
|
164
|
+
$stdout << "E"
|
165
|
+
end
|
166
|
+
|
167
|
+
$stdout.flush
|
168
|
+
end
|
169
|
+
|
170
|
+
@report.add [uri1, uri2], :diff => diff_avg
|
171
|
+
|
172
|
+
$stdout << "\n\n"
|
173
|
+
end
|
174
|
+
end
|