kronk 1.2.5 → 1.3.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 +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
|