diff --git a/README.md b/README.md index f34d385..d4df5fa 100644 --- a/README.md +++ b/README.md @@ -5,24 +5,6 @@ Gem with utilities used by Unity team # Documentation - ### Utils: - - **CiFormatter** - Custom rspec formatter: collapses pending tests and adds some time analytics. - - ``` - rspec --formatter CiFormatter - ...bash - Rubric publishing using Muppet::PublishJob - updating rubric - enqueues Muppet::PublishJob | Duration: 0.03981s - ... - Groups: - 7.20884s ./spec/services/topics_views_count/update_spec.rb (right after midnight) - 5.45071s ./spec/commands/ugc/approve_topic_spec.rb (approve logic) - ... - Single examples: - 5.45071s ./spec/commands/ugc/approve_topic_spec.rb (approve logic / creates a topic) - 4.91609s ./spec/commands/topic/update/reviews_of_published_spec.rb (has correct state value / is expected to be need approval) - ``` - - **Retrier** - Restarting passed block. ```ruby Unity::Utils::Retrier.call { 5 / 0 } @@ -106,6 +88,56 @@ Gem with utilities used by Unity team end ``` +- ### Rake tasks: + - **unity:rspec** - rspec with custom report format: collapses pending tests and adds some analytics. + + ```bash + bundle exec rake unity:rspec + bundle exec rake unity:rspec\['spec/queries'\] + bundle exec rake unity:rspec\['spec/{jobs\,services}/'\] + bundle exec rake unity:rspec\['spec/ --exclude-pattern "spec/{models\,requests}/**/*_spec.rb"'\] + ... + [TEST PROF INFO] TagProf report for type + + type time total %total %time avg + + query 00:27.908 134 15.14 37.51 00:00.208 + service 00:15.849 167 18.87 21.31 00:00.094 + ... + + Top 10 slowest examples (10.23 seconds, 13.7% of total time): + Api::V2::RssGeneratorJob generate for topic1 double generating does not duplicate data + 1.32 seconds ./spec/jobs/api/v2/rss_generator_job_spec.rb:39 + Exports::V2::BaseQuery behaves like queries/exports/v2/postponed when site is postponed is expected to contain + 1.2 seconds ./spec/support/shared_examples/queries/exports/v2/postponed_example.rb:28 + ... + + Top 10 slowest example groups: + Exports::V2::Indexnow::TopicsQuery + 0.89632 seconds average (5.38 seconds / 6 examples) ./spec/queries/exports/v2/indexnow/topics_query_spec.rb:5 + Exports::V2::RssAggregation + 0.70154 seconds average (5.61 seconds / 8 examples) ./spec/services/exports/rss_aggregation_spec.rb:5 + ... + + Finished in 1 minute 14.71 seconds (files took 7.29 seconds to load) + 885 examples, 0 failures, 31 pending + + Randomized with seed 6856 + + [TEST PROF INFO] Factories usage + + Total: 1624 + Total top-level: 1298 + Total time: 00:54.568 (out of 01:16.029) + Total uniq factories: 12 + + total top-level total time time per call top-level time name + + 521 521 8.9882s 0.0173s 8.9882s v2_topic + 394 394 17.5059s 0.0444s 17.5059s v2_push_topic + ... + ``` + ## Installation Add this line to your application's Gemfile: diff --git a/lib/unity-utils.rb b/lib/unity-utils.rb index e22886a..606cd71 100644 --- a/lib/unity-utils.rb +++ b/lib/unity-utils.rb @@ -1,3 +1,4 @@ # frozen_string_literal: true require_relative 'unity/utils' +require_relative 'unity/railtie' if defined?(Rails::Railtie) diff --git a/lib/unity/railtie.rb b/lib/unity/railtie.rb new file mode 100644 index 0000000..887f96c --- /dev/null +++ b/lib/unity/railtie.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require_relative 'spec/support/ci_formatter' +require_relative 'spec/support/tag_prof' + +module Unity + class Railtie < Rails::Railtie + railtie_name :unity_utils + + rake_tasks do + namespace :unity do + desc 'Run tests' + task :rspec, %i[options] => :environment do |_t, args| + system("FPROF=1 TAG_PROF=type bundle exec rspec #{args.options} --require test-prof --format CiFormatter --profile") + end + end + + task unity_utils: ['unity:rspec'] + end + end +end diff --git a/lib/unity/spec/support/ci_formatter.rb b/lib/unity/spec/support/ci_formatter.rb new file mode 100644 index 0000000..b70fc16 --- /dev/null +++ b/lib/unity/spec/support/ci_formatter.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'rspec/core' + +RSpec::Support.require_rspec_core 'formatters/progress_formatter' + +class CiFormatter < RSpec::Core::Formatters::ProgressFormatter + RSpec::Core::Formatters.register self + + def example_pending(_); end + + def dump_pending(_); end +end diff --git a/lib/unity/spec/support/tag_prof.rb b/lib/unity/spec/support/tag_prof.rb new file mode 100644 index 0000000..6ec7125 --- /dev/null +++ b/lib/unity/spec/support/tag_prof.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'rspec/core' + +# https://test-prof.evilmartians.io/profilers/tag_prof?id=pro-tip-more-types +RSpec.configure do |config| + config.define_derived_metadata(file_path: %r{/spec/}) do |metadata| + # do not overwrite type if it's already set + next if metadata.key?(:type) + + match = metadata[:location].match(%r{/spec/([^/]+)/}) + metadata[:type] = match[1].singularize.to_sym + end +end diff --git a/lib/unity/utils.rb b/lib/unity/utils.rb index a594813..57660b3 100644 --- a/lib/unity/utils.rb +++ b/lib/unity/utils.rb @@ -7,7 +7,6 @@ require_relative 'modules/loggable' # Utils -require_relative 'utils/ci_formatter' require_relative 'utils/faraday_with_retries' require_relative 'utils/retrier' require_relative 'utils/thread_pool' diff --git a/lib/unity/utils/ci_formatter.rb b/lib/unity/utils/ci_formatter.rb deleted file mode 100644 index 460aa03..0000000 --- a/lib/unity/utils/ci_formatter.rb +++ /dev/null @@ -1,93 +0,0 @@ -# frozen_string_literal: true - -require 'rspec/core' -require 'rspec/core/formatters/console_codes' - -RSpec::Support.require_rspec_core 'formatters/documentation_formatter' - -class CiFormatter < RSpec::Core::Formatters::DocumentationFormatter - RSpec::Core::Formatters.register self - - DURATION_LONG_FORMAT = '%-80s | Duration: %7.5fs' - DURATION_SHORT_FORMAT = '%.5fs' - SHOW_TOP = 20 - - def initialize(*args) - super - @example_times = [] - end - - def example_started(*_args) - @time = Time.zone.now - end - - def example_passed(notification) - super - - @example_times << [ - notification.example.file_path, - notification.example.example_group.description, - notification.example.description, - Time.zone.now - @time - ] - end - - def failure_output(example) - RSpec::Core::Formatters::ConsoleCodes.wrap( - format(DURATION_LONG_FORMAT, - "#{current_indentation}#{example.description.strip} (FAILED - #{next_failure_index})", - example.execution_result[:run_time]), - :failure - ) - end - - def passed_output(example) - RSpec::Core::Formatters::ConsoleCodes.wrap( - format(DURATION_LONG_FORMAT, - "#{current_indentation}#{example.description.strip}", - Time.zone.now - @time), - :success - ) - end - - def dump_summary(summary_notification) - dump_group_times - dump_example_times - - @output.flush - - super - end - - def example_pending(_); end - - def dump_pending(_); end - - private - - def dump_example_times - @output.puts "\n\nSingle examples:\n" - - sorted_by_time(@example_times)[0..SHOW_TOP].each do |file_path, group, example, time| - @output.puts "#{format(DURATION_SHORT_FORMAT, time)} #{file_path} (#{group} / #{example})" - end - end - - def dump_group_times - @output.puts "\n\nGroups:\n" - - group_times = Hash.new(0) - @example_times.each do |file_path, group, _example, time| - group_times["#{file_path} (#{group})"] += time - end - - sorted_by_time(group_times)[0..SHOW_TOP].each do |group, time| - @output.puts "#{format(DURATION_SHORT_FORMAT, time)} #{group}" - end - end - - # sorted by last ascending - def sorted_by_time(times) - times.to_a.sort_by(&:last).reverse - end -end diff --git a/lib/unity/version.rb b/lib/unity/version.rb index cc74e11..d23c09a 100644 --- a/lib/unity/version.rb +++ b/lib/unity/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module Unity - VERSION = '0.3.0' + VERSION = '0.4.0' end diff --git a/unity-utils.gemspec b/unity-utils.gemspec index b83cba0..ac068ee 100644 --- a/unity-utils.gemspec +++ b/unity-utils.gemspec @@ -21,6 +21,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'activesupport', '>= 4.2' spec.add_dependency 'faraday', '>= 1.0', '< 1.4' - spec.add_dependency 'rspec-core', '>= 3.9' + spec.add_dependency 'rspec-rails', '>= 5.0' spec.add_dependency 'ruby-progressbar', '>= 1.11.0' + spec.add_dependency 'test-prof', '~> 1.0' end