Advertisement
Guest User

Untitled

a guest
Mar 27th, 2017
39
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.46 KB | None | 0 0
  1. module FactoryGirl
  2. module Doctor
  3. module FloatDuration
  4. refine Float do
  5. def duration
  6. t = self
  7. format("%02d:%02d.%03d", t / 60, t % 60, t.modulo(1) * 1000)
  8. end
  9. end
  10. end
  11.  
  12. using FloatDuration
  13.  
  14. module FactoryExt
  15. def run(strategy = @strategy)
  16. Doctor.within_factory(strategy) { super }
  17. end
  18. end
  19.  
  20. class << self
  21. attr_reader :count, :time
  22.  
  23. def init
  24. @depth = 0
  25. reset
  26. FactoryGirl::FactoryRunner.prepend FactoryExt
  27. end
  28.  
  29. def within_factory(strategy)
  30. return yield if ignore?
  31. ts = Time.now if @depth.zero?
  32. @depth += 1
  33. @count += 1 if strategy == :create
  34. yield
  35. ensure
  36. @depth -= 1
  37.  
  38. if @depth.zero?
  39. delta = (Time.now - ts)
  40. @time += delta
  41. end
  42. end
  43.  
  44. def ignore
  45. @ignored = true
  46. res = yield
  47. @ignored = false
  48. res
  49. end
  50.  
  51. def reset
  52. @count = 0
  53. @time = 0.0
  54. end
  55.  
  56. def within_factory?
  57. @depth.positive?
  58. end
  59.  
  60. def ignore?
  61. @ignored == true
  62. end
  63. end
  64.  
  65. class Profiler
  66. IGNORED_QUERIES_PATTERN = %r{(
  67. pg_table|
  68. pg_attribute|
  69. pg_namespace|
  70. show\stables|
  71. pragma|
  72. sqlite_master/rollback|
  73. \ATRUNCATE TABLE|
  74. \AALTER TABLE|
  75. \ABEGIN|
  76. \ACOMMIT|
  77. \AROLLBACK|
  78. \ARELEASE|
  79. \ASAVEPOINT
  80. )}xi
  81.  
  82. NOTIFICATIONS = [:example_started, :example_finished].freeze
  83.  
  84. def initialize
  85. @queries = []
  86. @example_groups = Hash.new { |h, k| h[k] = [] }
  87.  
  88. ActiveSupport::Notifications.subscribe('sql.active_record') do |_name, _start, _finish, _id, query|
  89. next if Doctor.within_factory?
  90. next if query[:sql] =~ IGNORED_QUERIES_PATTERN
  91. @queries << query[:sql]
  92. end
  93. end
  94.  
  95. def example_started(_notification)
  96. @queries.clear
  97. Doctor.reset
  98. end
  99.  
  100. def example_finished(notification)
  101. return if notification.example.pending?
  102.  
  103. if Doctor.count.positive? && @queries.size.zero?
  104. group = notification.example.example_group.parent_groups.last
  105. notification.example.metadata.merge!(
  106. factories: Doctor.count,
  107. time: Doctor.time
  108. )
  109. @example_groups[group] << notification.example
  110. end
  111. end
  112.  
  113. def print
  114. return if @example_groups.empty?
  115. output.puts(
  116. "\n\nFactoryDoctor found useless data generation "\
  117. "in the following examples\n"
  118. )
  119.  
  120. total_time = 0.0
  121.  
  122. @example_groups.each do |group, examples|
  123. out = ["#{group.description} (#{group.metadata[:location]})"
  124. examples.each do |ex|
  125. total_time += ex.metadata[:time]
  126. out << " #{ex.description} (#{ex.metadata[:location]}) – #{ex.metadata[:factories]} created objects, #{ex.metadata[:time].duration}"
  127. end
  128. output.puts out.join("\n")
  129. end
  130.  
  131. output.puts "\nTotal wasted time: #{total_time.duration}\n"
  132. end
  133.  
  134. private
  135.  
  136. def output
  137. RSpec.configuration.output_stream
  138. end
  139. end
  140. end
  141. end
  142.  
  143. if ENV['FDOC']
  144. FactoryGirl::Doctor.init
  145.  
  146. RSpec.configure do |config|
  147. listener = FactoryGirl::Doctor::Profiler.new
  148.  
  149. config.reporter.register_listener(listener, *FactoryGirl::Doctor::Profiler::NOTIFICATIONS)
  150.  
  151. config.after(:suite) { listener.print }
  152. end
  153. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement