Skip to content

Commit

Permalink
Merge pull request #1 from procore-oss/BL/zach-fork-updates
Browse files Browse the repository at this point in the history
Bl/zach fork updates
  • Loading branch information
pc-bob authored Feb 8, 2024
2 parents a69a4d6 + 28d58ce commit e775cf9
Show file tree
Hide file tree
Showing 15 changed files with 246 additions and 30 deletions.
45 changes: 42 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ which results will be collected.
bundle exec rake rspec_profiling:install
```

If you are planning on using `sqlite` or `pg` ensure to add the depency to your gemfile

```
gem 'sqlite', require: false
gem 'pg', require: false
```

## Usage

### Choose a version control system
Expand Down Expand Up @@ -74,14 +81,46 @@ RspecProfiling.configure do |config|
end
```

#### Custom Event Subscriptions

```Ruby
RspecProfiling.configure do |config|
config.events = %w[event1 event2]
end
```

Note that custom events are only currenly reported by the CSV collector.

#### Custom Event Recording

It is possible to record the event metadata for a spec.

```Ruby
describe 'Records all active record queries', record_events: %w[sql.active_record] do
it 'Records Rails deprecations', record_events: %w[deprecation.rails] do
...
end
it 'Records nothing' do
...
end
end
```

### Choose a results collector

Results are collected just by running the specs.

#### SQLite3

By default, profiles are collected in an SQL database. Make sure you've
run the installation rake task before attempting.
Make sure you've run the installation rake task before attempting.

You can configure `RspecProfiling` to collect results in a SQL database in `config/initializers/rspec_profiling.rb`:

```Ruby
RspecProfiling.configure do |config|
config.collector = RspecProfiling::Collectors::SQL
end
```

You can review results by running the RspecProfiling console.
The console has a preloaded `results` variable.
Expand Down Expand Up @@ -112,7 +151,7 @@ debugging, such as `exception` and `status`.

#### CSV

You can configure `RspecProfiling` to collect results in a CSV in `config/initializers/rspec_profiling.rb`:
By default, profiles are collected in an a CSV file. You can configure `RspecProfiling` to collect results in a CSV in `config/initializers/rspec_profiling.rb`:

```Ruby
RspecProfiling.configure do |config|
Expand Down
16 changes: 14 additions & 2 deletions lib/rspec_profiling.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,24 @@
require "rspec_profiling/version"
require "rspec_profiling/run"
require "rspec_profiling/collectors/csv"
require "rspec_profiling/collectors/sql"
require "rspec_profiling/collectors/psql"
require "rspec_profiling/vcs/git"
require "rspec_profiling/vcs/svn"
require "rspec_profiling/vcs/git_svn"

begin
require "rspec_profiling/collectors/sql"
rescue LoadError
#no op
end

begin
require "rspec_profiling/collectors/psql"
rescue LoadError
#no op
end



module RspecProfiling
class Railtie < Rails::Railtie
railtie_name :rspec_profiling
Expand Down
37 changes: 30 additions & 7 deletions lib/rspec_profiling/collectors/csv.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class CSV
HEADERS = %w{
branch
commit_hash
seed
date
file
line_number
Expand All @@ -17,6 +18,8 @@ class CSV
query_time
request_count
request_time
start_memory
end_memory
}

def self.install
Expand All @@ -31,24 +34,44 @@ def self.reset
# no op
end

def initialize
RspecProfiling.config.csv_path ||= 'tmp/spec_benchmarks.csv'
def initialize(config=RspecProfiling.config)
config.csv_path ||= 'tmp/spec_benchmarks.csv'

@config = config
end

def insert(attributes)
output << HEADERS.map do |field|
attributes.fetch(field.to_sym)
end
output << static_cells(attributes) + event_cells(attributes)
end

private

attr_reader :config

def output
@output ||= ::CSV.open(path, "w").tap { |csv| csv << HEADERS }
@output ||= ::CSV.open(path, "w").tap { |csv| csv << HEADERS + event_headers }
end

def path
RspecProfiling.config.csv_path.call
config.csv_path.call
end

def static_cells(attributes)
HEADERS.map do |field|
attributes.fetch(field.to_sym)
end
end

def event_headers
config.events.flat_map do |event|
["#{event}_count", "#{event}_time", "#{event}_events"]
end
end

def event_cells(attributes)
config.events.flat_map do |event|
[attributes[:event_counts][event], attributes[:event_times][event], attributes[:event_events][event].to_json]
end
end
end
end
Expand Down
70 changes: 70 additions & 0 deletions lib/rspec_profiling/collectors/json.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
module RspecProfiling
module Collectors
class JSON
KEYS = %w{
branch
commit_hash
seed
date
file
line_number
description
status
exception
time
query_count
query_time
request_count
request_time
start_memory
end_memory
}

def self.install
# no op
end

def self.uninstall
# no op
end

def self.reset
# no op
end

def initialize(config=RspecProfiling.config)
config.output_file_path ||= 'tmp/spec_benchmarks.json'

@config = config
end

def insert(attributes)
output << merge_attributes_and_events(attributes) + "\n"
end

private

attr_reader :config

def output
@output ||= ::File.open(path, "w")
end

def path
config.output_file_path.call
end

def merge_attributes_and_events(attributes)
config.events.flat_map do |event|
attributes["#{event}_counts"] = attributes[:event_counts][event]
attributes["#{event}_times"] = attributes[:event_times][event]
attributes["#{event}_events"] = attributes[:event_events][event]
end

attributes.merge!(config.additional_data)

attributes.except(:event_counts, :event_times, :event_events, :events).to_json
end
end
end
end
2 changes: 1 addition & 1 deletion lib/rspec_profiling/collectors/psql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def uninstall
end

def insert(attributes)
results.create!(attributes.except(:created_at))
results.create!(attributes.except(:created_at, :events, :event_counts, :event_times, :event_events))
end

def results
Expand Down
2 changes: 1 addition & 1 deletion lib/rspec_profiling/collectors/sql.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def uninstall
end

def insert(attributes)
results.create!(attributes.except(:created_at))
results.create!(attributes.except(:created_at, :events, :event_counts, :event_times, :event_events))
end

def results
Expand Down
6 changes: 4 additions & 2 deletions lib/rspec_profiling/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ def self.configure

def self.config
@config ||= OpenStruct.new({
collector: RspecProfiling::Collectors::SQL,
collector: RspecProfiling::Collectors::CSV,
vcs: RspecProfiling::VCS::Git,
table_name: 'spec_profiling_results'
table_name: 'spec_profiling_results',
events: [],
additional_data: {}
})
end
end
24 changes: 23 additions & 1 deletion lib/rspec_profiling/example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class Example
def initialize(example)
@example = example
@counts = Hash.new(0)
@event_counts = Hash.new(0)
@event_times = Hash.new(0)
@event_events = Hash.new()
end

def file
Expand All @@ -38,7 +41,7 @@ def description
def status
execution_result.status
end

def exception
execution_result.exception
end
Expand All @@ -63,6 +66,8 @@ def request_time
counts[:request_time]
end

attr_reader :event_counts, :event_times, :event_events

def log_query(query, start, finish)
unless query[:sql] =~ IGNORED_QUERIES_PATTERN
counts[:query_count] += 1
Expand All @@ -75,6 +80,19 @@ def log_request(request, start, finish)
counts[:request_time] += request[:view_runtime].to_f
end

def log_event(event_name, event, start, finish)
event_counts[event_name] += 1
event_times[event_name] += (finish - start)
event_events[event_name] ||= []
if verbose_record_event?(event_name)
begin
event_events[event_name] << event.as_json
rescue => e
# no op
end
end
end

private

attr_reader :example, :counts
Expand All @@ -90,5 +108,9 @@ def execution_result
def metadata
example.metadata
end

def verbose_record_event?(event_name)
metadata[:record_events].to_a.include?(event_name)
end
end
end
2 changes: 1 addition & 1 deletion lib/rspec_profiling/rspec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require "rspec_profiling"

RSpec.configure do |config|
runner = RspecProfiling::Run.new(RspecProfiling.config.collector.new,
runner = RspecProfiling::Run.new(RspecProfiling.config.collector.new,
RspecProfiling.config.vcs.new)

config.reporter.register_listener(
Expand Down
Loading

0 comments on commit e775cf9

Please sign in to comment.