# Benchmark for the cost of resolving inline attachments while formatting a text.
#
#   bin/rails r bench_inline_attachments_scrubber.rb
#
# Formats a text of about 100 nodes whose container has 100 attachments.
# It goes through textilizable, so it measures the same thing on 6.x, where
# inline attachments are resolved by ApplicationHelper#parse_inline_attachments,
# and on 7.x, where they are resolved by InlineAttachmentsScrubber.
#
# Nothing is written: the attachments are unsaved in-memory records and the
# text formatting setting is overridden in this process only.

CHUNKS = 4   # about 100 nodes
FILES  = 100
REPS   = 20

def Setting.text_formatting = 'common_mark'

CHUNK = <<~MD
  ## Section %<i>d

  Some paragraph with *emphasis*, `code` and a [link](https://example.com).

  * first item
  * second item

  ![](img0.png)
MD

TEXT = (1..CHUNKS).map {|i| format(CHUNK, :i => i)}.join("\n")

# Elements and text nodes alike: the formatter walks both.
def node_count(html)
  count = 0
  Loofah.fragment(html).scrub!(Loofah::Scrubber.new {|_node| count += 1})
  count
end

attachments =
  Array.new(FILES) do |i|
    Attachment.new(:filename => "img#{i}.png", :created_on => Time.now - i).tap {|a| a.id = i + 1}
  end

controller = ApplicationController.new
controller.request = ActionDispatch::TestRequest.create
view = controller.view_context

render = -> {view.textilizable(TEXT, :attachments => attachments, :only_path => true)}

html = render.call # warm up

GC.start
objects = GC.stat(:total_allocated_objects)
started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
REPS.times {render.call}
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started
objects = GC.stat(:total_allocated_objects) - objects

abort 'the inline attachment was not resolved' unless html.include?('/attachments/download/1/img0.png')

puts "Redmine #{Redmine::VERSION}, Ruby #{RUBY_VERSION}, #{Setting.text_formatting}"
puts "#{node_count(html)} nodes, #{FILES} attachments, #{REPS} renderings"
puts format('%.1f ms and %d objects allocated per rendering', elapsed / REPS * 1000, objects / REPS)
