perf_check 0.1.6 → 0.1.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e429268ea248d238130f873494e04bdf8b74a3fd
4
- data.tar.gz: 49cd55f1113f6f31492a84c273d58761d1ce824f
3
+ metadata.gz: 55dd84ea36a6406e768ed5581acf65b9f4b6aa50
4
+ data.tar.gz: fb5b16a1aea5f51c75af09c9843d4bd3a063ff30
5
5
  SHA512:
6
- metadata.gz: 263a12324d08e02724e6ef92703a02a0ea13e5cf07c846550189c628aaba177c5662492089745d6f172e5cab2ef5be7e46039ee6ed8100f6ebc8cf1f6bcd9872
7
- data.tar.gz: 56b851ddfd60cc6192c04cedafc2c715983cf01c930135da2881d2bacc5b62edc2eaac70092c68e2f2b14c404f9003f4596c8ba1ce893ba62a771669225138f6
6
+ metadata.gz: 1377387b9ef80a3bbbc9128b4461bf89ffb189fcbee5f2f685becee729681affb51a193725a5bcc414f0ac9eb57176c010fb89bd4a1a403ee8194f33aadbabc0
7
+ data.tar.gz: 64d8c600ad45a422698f31717e0612a150f7899d7252cf60c4c9c9fb70f531e13445605a9783c126dcaa1c30b06d9c5706ccf19bab9651c1e612aa24b7f78ba5
data/bin/perf_check CHANGED
@@ -8,7 +8,8 @@ options.login = nil
8
8
  options.number_of_requests = 10
9
9
  options.reference = 'master'
10
10
  options.http_statuses = [200]
11
-
11
+ options.verify_responses = false
12
+ options.caching = true
12
13
 
13
14
  opts = OptionParser.new do |opts|
14
15
  opts.banner = "Usage: perf_check [options] [route ...]"
@@ -52,7 +53,11 @@ opts = OptionParser.new do |opts|
52
53
  end
53
54
 
54
55
  opts.on('--no-caching', 'Do not enable fragment caching') do
55
- ENV['PERF_CHECK_NOCACHING'] = 'true'
56
+ options.caching = false
57
+ end
58
+
59
+ opts.on('--fail-fast', '-f', 'Bail immediately on non-200 HTTP response') do
60
+ options[:fail_fast?] = true
56
61
  end
57
62
 
58
63
  opts.on('--302-success', 'Consider HTTP 302 code a successful request') do
@@ -63,6 +68,29 @@ opts = OptionParser.new do |opts|
63
68
  options.http_statuses.delete(302)
64
69
  end
65
70
 
71
+ opts.separator "\nMisc"
72
+ opts.on('--input FILE', '-i') do |input|
73
+ File.readlines(input).each do |resource|
74
+ ARGV << resource.strip
75
+ end
76
+ end
77
+
78
+ opts.on('--verify-responses',
79
+ 'Check whether there is a diff between the responses of this and the reference branch') do
80
+ options.verify_responses = true
81
+ end
82
+
83
+ opts.on('--brief', '-b') do
84
+ options.brief = true
85
+ end
86
+
87
+ opts.on('--diff') do
88
+ options.diff = true
89
+ options.brief = true
90
+ options.verify_responses = true
91
+ options.number_of_requests = 1
92
+ end
93
+
66
94
  opts.separator ''
67
95
  opts.separator <<EOF
68
96
  Usage examples:
@@ -70,6 +98,7 @@ Usage examples:
70
98
  perf_check /user/45/posts
71
99
  perf_check /user/45/posts -n5
72
100
  perf_check /user/45/posts --standard
101
+ perf_check /user/45/posts --admin
73
102
 
74
103
  Benchmark against a specific commit
75
104
  perf_check /user/45/posts -r 0123abcdefg
@@ -77,11 +106,20 @@ Usage examples:
77
106
 
78
107
  Benchmark the changes in the working tree
79
108
  perf_check /user/45/posts -r HEAD
109
+
110
+ Benchmark and diff the output against master
111
+ perf_check /user/45/posts --verify-responses
112
+
113
+ Just diff the output on your branch with master
114
+ perf_check /user/45/posts --diff
115
+
116
+ Diff a bunch of urls listed in a file (newline seperated)
117
+ perf_check --diff --input FILE
80
118
  EOF
81
119
  end
82
120
  opts.parse!
83
121
 
84
- PerfCheck.require_rails
122
+ PerfCheck.require_rails(options)
85
123
 
86
124
  ARGV.each do |route|
87
125
  perf_check.add_test_case(route)
@@ -94,7 +132,11 @@ end
94
132
  perf_check.sanity_check
95
133
  perf_check.run
96
134
 
97
- perf_check.print_results
135
+ if options.brief
136
+ perf_check.print_brief_results
137
+ else
138
+ perf_check.print_results
139
+ end
98
140
 
99
141
  # _______________________
100
142
  # < You made it faster!!! >
@@ -32,7 +32,6 @@ class PerfCheck
32
32
  system('git stash -q >/dev/null')
33
33
  abort("Problem with git stash! Bailing...") unless $?.success?
34
34
  at_exit do
35
- system('git checkout .')
36
35
  Git.pop
37
36
  end
38
37
  end
@@ -21,6 +21,10 @@ class PerfCheck
21
21
  config = ActiveSupport::Configurable::Configuration
22
22
  config.send(:define_method, :perform_caching){ fragment_caching }
23
23
  end
24
+
25
+ if ENV['PERF_CHECK'] && ENV['PERF_CHECK_VERIFICATION']
26
+ PerfCheck::Server.seed_random!
27
+ end
24
28
  end
25
29
  end
26
30
  end
@@ -12,7 +12,7 @@ class PerfCheck
12
12
 
13
13
  def self.authorization_action(method, login_route, &block)
14
14
  Rails.application.reload_routes!
15
- p = Rails.application.routes.recognize_path(login_route, :method => method)
15
+ p = PerfCheck::Server.recognize_path(login_route, :method => method)
16
16
  controller = "#{p[:controller]}_controller".classify.constantize
17
17
  action = p[:action]
18
18
 
@@ -35,6 +35,49 @@ class PerfCheck
35
35
  end
36
36
  end
37
37
 
38
+ def self.mounted_path(engine)
39
+ route = Rails.application.routes.routes.detect do |route|
40
+ route.app == engine
41
+ end
42
+ route && route.path
43
+ end
44
+
45
+ def self.recognize_path(path, opts={})
46
+ Rails::Engine.subclasses.each do |engine|
47
+ if match = mounted_path(engine) =~ path
48
+ path = path.sub(match.to_s, '')
49
+ return engine.routes.recognize_path(path, opts)
50
+ end
51
+ end
52
+ Rails.application.routes.recognize_path(path, opts)
53
+ end
54
+
55
+ def self.seed_random!
56
+ # Seed random
57
+ srand(1)
58
+
59
+ # SecureRandom cannot be seeded, so we have to monkey patch it instead :\
60
+ def SecureRandom.hex(n=16)
61
+ '4' * n
62
+ end
63
+
64
+ def SecureRandom.random_bytes(n=16)
65
+ '4' * n
66
+ end
67
+
68
+ def SecureRandom.random_number(n=0)
69
+ n > 4 ? 4 : n
70
+ end
71
+
72
+ def SecureRandom.urlsafe_base64(n=16, padding=false)
73
+ '4' * (4*n / 3)
74
+ end
75
+
76
+ def SecureRandom.uuid
77
+ "00000000-0000-0000-0000-000000000004"
78
+ end
79
+ end
80
+
38
81
  def self.sign_cookie_data(key, data, opts={})
39
82
  opts[:serializer] ||= Marshal
40
83
  secret = Rails.application.config.secret_token
@@ -116,10 +159,10 @@ class PerfCheck
116
159
 
117
160
  def restart
118
161
  if !@running
119
- $stderr.print "starting rails..."
162
+ $stderr.print "starting rails...\n"
120
163
  start
121
164
  else
122
- $stderr.print "re-starting rails..."
165
+ $stderr.print "re-starting rails...\n"
123
166
  exit
124
167
  start
125
168
  end
@@ -1,17 +1,18 @@
1
1
  # coding: utf-8
2
2
 
3
+ require 'diffy'
4
+
3
5
  class PerfCheck
4
6
  class TestCase
5
7
  attr_accessor :resource, :controller, :action, :format
6
- attr_accessor :latencies, :cookie
7
- attr_accessor :this_latencies, :reference_latencies
8
+ attr_accessor :cookie, :this_response, :reference_response
9
+ attr_accessor :this_profiles, :reference_profiles
8
10
 
9
11
  def initialize(route)
10
- params = Rails.application.routes.recognize_path(route)
12
+ params = PerfCheck::Server.recognize_path(route)
11
13
 
12
- self.this_latencies = []
13
- self.reference_latencies = []
14
- self.latencies = this_latencies
14
+ self.this_profiles = []
15
+ self.reference_profiles = []
15
16
 
16
17
  self.controller = params[:controller].split('/')[-1]
17
18
  self.action = params[:action]
@@ -19,11 +20,20 @@ class PerfCheck
19
20
  self.resource = route
20
21
  end
21
22
 
23
+ def switch_to_reference_context
24
+ @context = :reference
25
+ end
26
+
22
27
  def run(server, options)
23
- print("\t"+'request #'.underline)
24
- print(" "+'latency'.underline)
25
- print(" "+'server rss'.underline)
26
- puts(" "+'profiler data'.underline)
28
+ unless options.diff
29
+ print("\t"+'request #'.underline)
30
+ print(" "+'latency'.underline)
31
+ print(" "+'server rss'.underline)
32
+ print(" "+'status'.underline)
33
+ puts(" "+'profiler data'.underline)
34
+ end
35
+
36
+ profiles = (@context == :reference) ? reference_profiles : this_profiles
27
37
 
28
38
  headers = {'Cookie' => "#{cookie}"}
29
39
  unless self.format
@@ -35,31 +45,47 @@ class PerfCheck
35
45
  end
36
46
 
37
47
  unless options.http_statuses.include? profile.response_code
38
- File.open("tmp/perf_check/failed_request.html", 'w') do |error_dump|
39
- error_dump.write(profile.response_body)
48
+ if options.fail_fast?
49
+ File.open("tmp/perf_check/failed_request.html", 'w') do |error_dump|
50
+ error_dump.write(profile.response_body)
51
+ end
52
+ error = sprintf("\t%2i:\tFAILED! (HTTP %d)", i, profile.response_code)
53
+ puts(error.red.bold)
54
+ puts("\t The server responded with a non-2xx status for this request.")
55
+ puts("\t The response has been written to tmp/perf_check/failed_request.html")
56
+ exit(1)
40
57
  end
41
- error = sprintf("\t%2i:\tFAILED! (HTTP %d)", i, profile.response_code)
42
- puts(error.red.bold)
43
- puts("\t The server responded with a non-2xx status for this request.")
44
- puts("\t The response has been written to tmp/perf_check/failed_request.html")
45
- exit(1)
46
58
  end
47
59
 
48
60
  next if i.zero?
49
- printf("\t%2i:\t %.1fms %4dMB\t %s\n",
50
- i, profile.latency, server.mem, profile.profile_url)
51
61
 
52
- self.latencies << profile.latency
62
+ if options.verify_responses
63
+ if i == 1
64
+ if @context == :reference
65
+ self.reference_response = profile.response_body
66
+ else
67
+ self.this_response = profile.response_body
68
+ end
69
+ end
70
+ end
71
+
72
+ unless options.diff
73
+ printf("\t%2i:\t %.1fms %4dMB\t %s\t %s\n",
74
+ i, profile.latency, server.mem,
75
+ profile.response_code, profile.profile_url)
76
+ end
77
+
78
+ profiles << profile
53
79
  end
54
- puts
80
+ puts unless options.diff # pretty!
55
81
  end
56
82
 
57
83
  def this_latency
58
- this_latencies.inject(0.0, :+) / this_latencies.size
84
+ this_profiles.map(&:latency).inject(0.0, :+) / this_profiles.size
59
85
  end
60
86
 
61
87
  def reference_latency
62
- reference_latencies.inject(0.0, :+) / reference_latencies.size
88
+ reference_profiles.map(&:latency).inject(0.0, :+) / reference_profiles.size
63
89
  end
64
90
 
65
91
  def latency_difference
@@ -70,6 +96,22 @@ class PerfCheck
70
96
  reference_latency / this_latency
71
97
  end
72
98
 
99
+ def response_diff
100
+ diff = Diffy::Diff.new(this_response, reference_response,
101
+ :diff => PerfCheck.diff_options)
102
+ if diff.to_s.empty?
103
+ OpenStruct.new(:changed? => false)
104
+ else
105
+ FileUtils.mkdir_p("#{Rails.root}/tmp/perf_check/diffs")
106
+ file = `mktemp -u "#{Rails.root}/tmp/perf_check/diffs/XXXXXXXXXX"`.strip
107
+ [:text, :html].each do |format|
108
+ ext = {:text => 'diff', :html => 'html'}[format]
109
+ File.open("#{file}.#{ext}", 'w'){ |f| f.write(diff.to_s(format)) }
110
+ end
111
+ OpenStruct.new(:changed? => true, :file => "#{file}.diff")
112
+ end
113
+ end
114
+
73
115
  def eql?(test)
74
116
  resource == test.resource
75
117
  end
data/lib/perf_check.rb CHANGED
@@ -11,8 +11,19 @@ require 'colorize'
11
11
  class PerfCheck
12
12
  attr_accessor :options, :server, :test_cases
13
13
 
14
- def self.require_rails
14
+ def self.diff_options
15
+ @@diff_options ||=
16
+ ['-U3', '--ignore-matching-lines=/mini-profiler-resources/includes.js']
17
+ end
18
+
19
+ def self.require_rails(options)
15
20
  ENV['PERF_CHECK'] = '1'
21
+ if options.verify_responses
22
+ ENV['PERF_CHECK_VERIFICATION'] = '1'
23
+ end
24
+ if !options.caching
25
+ ENV['PERF_CHECK_NOCACHING'] = '1'
26
+ end
16
27
 
17
28
  app_root = Dir.pwd
18
29
  until app_root == '/' || File.exist?("#{app_root}/config/application.rb")
@@ -57,28 +68,59 @@ class PerfCheck
57
68
  if i == 1
58
69
  Git.stash_if_needed
59
70
  Git.checkout_reference(options.reference)
60
- test_cases.each{ |x| x.latencies = x.reference_latencies }
71
+ test_cases.each{ |x| x.switch_to_reference_context }
61
72
  end
62
73
 
63
- test_cases.each do |test|
64
- server.restart
74
+ server.restart
75
+ test_cases.each_with_index do |test, i|
76
+ server.restart unless i.zero? || options.diff
65
77
 
66
78
  if options.login
67
79
  test.cookie = server.login(options.login, test)
68
80
  end
69
81
 
70
- puts("\n\nBenchmarking #{test.resource}:")
82
+ if options.diff
83
+ puts "Issuing #{test.resource}"
84
+ else
85
+ puts("\nBenchmarking #{test.resource}:")
86
+ end
71
87
  test.run(server, options)
72
88
  end
73
89
  end
74
90
  end
75
91
 
92
+
93
+ def print_diff_results(diff)
94
+ if diff.changed?
95
+ print(" Diff: #{diff.file}".bold.light_red)
96
+ else
97
+ print(" Diff: Output is identical!".bold.light_green)
98
+ end
99
+ end
100
+
101
+ def print_brief_results
102
+ test_cases.each do |test|
103
+ print(test.resource.ljust(40) + ': ')
104
+
105
+ codes = (test.this_profiles+test.reference_profiles).map(&:response_code).uniq
106
+ print("(HTTP "+codes.join(',')+") ")
107
+
108
+ printf('%.1fms', test.this_latency)
109
+
110
+ puts && next if test.reference_profiles.empty?
111
+
112
+ print(sprintf(' (%+5.1fms)', test.latency_difference).bold)
113
+ print_diff_results(test.response_diff) if options.verify_responses
114
+ puts
115
+ end
116
+ end
117
+
76
118
  def print_results
77
119
  puts("==== Results ====")
78
120
  test_cases.each do |test|
79
- puts(test.resource)
121
+ puts(test.resource.bold)
80
122
 
81
- if test.reference_latencies.empty?
123
+ if test.reference_profiles.empty?
82
124
  printf("your branch: ".rjust(15)+"%.1fms\n", test.this_latency)
83
125
  next
84
126
  end
@@ -110,6 +152,8 @@ class PerfCheck
110
152
  puts("master: ".rjust(15) + "#{master_latency}")
111
153
  puts("your branch: ".rjust(15)+ "#{this_latency}")
112
154
  puts(("change: ".rjust(15) + "#{formatted_change}").bold.send(color))
155
+
156
+ print_diff_results(test.response_diff) if options.verify_responses
113
157
  end
114
158
  end
115
159
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perf_check
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - rubytune
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.7.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: diffy
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.5
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 3.0.5
27
41
  description:
28
42
  email:
29
43
  executables: