lib/honey_format/cli/benchmark_cli.rb
# frozen_string_literal: true
require 'optparse'
require 'honey_format/cli/result_writer'
module HoneyFormat
# Benchmark CLI
# @attr_reader [Hash] options from command line arguments
# @attr_reader [CLIResultWriter] writer the CLI result writer
class BenchmarkCLI
# CSV default test data location
CSV_TEST_DATA_URL = 'https://gist.github.com/buren/b669dd82fa37e37672da2cab33c8a830/raw/54ba14a698941ff61f3b854b66df0a7782c79c85/csv_1000_rows.csv'.freeze
# CSV default test data cache location
CSV_TEST_DATA_CACHE_PATH = '/tmp/honey-format-benchmark-test.csv'.freeze
attr_reader :writer, :options
# Instantiate the CLI
# @param writer [CLIResultWriter] the result writer to use
def initialize(writer: CLIResultWriter.new)
@used_input_path = nil
@writer = writer
@options = parse_options(argv: ARGV)
writer.verbose = true if @options[:verbose]
end
# Returns the expected runtime in seconds
# @param report_count [Integer] number of reports in benchmark
# @return [Integer] expected runtime in seconds
def expected_runtime_seconds(report_count:)
runs = report_count * options[:lines_multipliers].length
warmup_time_seconds = runs * options[:benchmark_warmup]
bench_time_seconds = runs * options[:benchmark_time]
warmup_time_seconds + bench_time_seconds
end
# Return the input path used for the benchmark
# @return [String] the input path (URL or filepath)
def used_input_path
options[:input_path] || @used_input_path
end
# Download or fetch the default benchmark file from cache
# @return [String] CSV file as a string
def fetch_default_benchmark_csv
cache_path = CSV_TEST_DATA_CACHE_PATH
if File.exist?(cache_path)
writer.puts "Cache file found at #{cache_path}.", verbose: true
@used_input_path = cache_path
return File.read(cache_path)
end
writer.print 'Downloading test data file from GitHub..', verbose: true
require 'open-uri'
open(CSV_TEST_DATA_URL).read.tap do |csv| # rubocop:disable Security/Open
@used_input_path = CSV_TEST_DATA_URL
writer.puts 'done!', verbose: true
File.write(cache_path, csv)
writer.puts "Wrote cache file to #{cache_path}..", verbose: true
end
end
# Parse command line arguments and return options
# @param [Array<String>] argv the command lines arguments
# @return [Hash] the command line options
def parse_options(argv:)
input_path = nil
benchmark_time = 30
benchmark_warmup = 5
lines_multipliers = [1]
verbose = false
OptionParser.new do |parser|
parser.banner = 'Usage: bin/benchmark [file.csv] [options]'
parser.default_argv = argv
parser.on('--csv=[file1.csv]', String, 'CSV file(s)') do |value|
input_path = value
end
parser.on('--[no-]verbose', 'Verbose output') do |value|
verbose = value
end
parser.on('--lines-multipliers=[1,10,50]', Array, 'Multiply the rows in the CSV file (default: 1)') do |value|
lines_multipliers = value.map do |v|
Integer(v).tap do |int|
unless int >= 1
raise(ArgumentError, '--lines-multiplier must be 1 or greater')
end
end
end
end
parser.on('--time=[30]', String, 'Benchmark time (default: 30)') do |value|
benchmark_time = Integer(value)
end
parser.on('--warmup=[30]', String, 'Benchmark warmup (default: 30)') do |value|
benchmark_warmup = Integer(value)
end
parser.on('-h', '--help', 'How to use') do
puts parser
exit
end
# No argument, shows at tail. This will print an options summary.
parser.on_tail('-h', '--help', 'Show this message') do
puts parser
exit
end
end.parse!
{
input_path: input_path,
benchmark_time: benchmark_time,
benchmark_warmup: benchmark_warmup,
lines_multipliers: lines_multipliers,
verbose: verbose,
}
end
end
end