123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- require "option_parser"
- require "./stats_lib"
- require "./expected_output"
- module Stats
- @@configs = [
- Config.common_mark_config,
- Config.gfm_config,
- ]
- # Set this to `true` an rerun `--update-files` to ease finding easy strict
- # fixes.
- @@improve_strict = false
- @@scores = Hash(String, Hash(Int32, CompareLevel)).new
- @@section_name_replace = Regex.new("[ \\)\\(]+")
- def self.process_config(test_prefix : String,
- raw : Bool,
- update_files : Bool,
- verbose : Bool,
- specified_section : String?,
- verbose_loose : Bool)
- config = @@configs.find! { |c| c.prefix == test_prefix }
- sections = load_common_mark_sections(test_prefix)
- scores = Hash(String, Hash(Int32, CompareLevel)).new
- sections.each do |key, value|
- next if !specified_section.nil? && key != specified_section
- units = [] of DataCase
- value.each do |e|
- result = compare_result(
- config,
- e,
- verbose_fail: verbose,
- verbose_loose_match: verbose_loose,
- extensions: e.extensions
- )
- units << DataCase.new(
- front_matter: result.test_case.to_s,
- input: result.test_case.markdown,
- expected_output: (@@improve_strict && result.compare_level == CompareLevel::Loose) ? result.test_case.html : result.result.not_nil!
- )
- if scores[key]?
- nested_map = scores[key]
- else
- scores[key] = Hash(Int32, CompareLevel).new
- nested_map = scores[key]
- end
- nested_map[e.example] = result.compare_level
- end
- if update_files && units.empty? == false
- file_name = key.downcase.gsub(@@section_name_replace, "_")
- file_name = file_name.rstrip('_')
- file_name = "#{file_name}.unit"
- Dir.mkdir_p Path["spec", test_prefix]
- File.write(Path["spec", test_prefix, file_name], unit_output(units))
- end
- end
- if raw || update_files
- print_raw(test_prefix, scores, update_files)
- end
- if !raw || update_files
- print_friendly(test_prefix, scores, update_files)
- end
- end
- def self.unit_output(cases : Array(DataCase)) : String
- _cases = cases.map do |data_case|
- ">>> #{data_case.front_matter}
- #{data_case.input}<<<
- #{data_case.expected_output}"
- end
- _cases.join
- end
- def self.pct(value : Int, total : Int, section : String) : String
- "#{value.to_s.rjust(4)} " +
- "of #{total.to_s.rjust(4)} " +
- "- #{(100 * value / total).format('.', "", 1).rjust(5)}% #{section}"
- end
- def self.print_raw(test_prefix : String, scores : Hash(String, Hash(Int32, CompareLevel)), update_files : Bool) : Nil
- sink : IO::FileDescriptor
- if update_files
- file = get_stats_file(test_prefix)
- puts "Updating #{file.path}"
- sink = file
- else
- sink = STDOUT
- end
- val = JSON.build(" ") do |json|
- json.object do
- scores.each do |section, map|
- json.string section
- json.object do
- map.each do |example, level|
- json.field example, level
- end
- end
- end
- end
- end
- sink.puts val
- sink.flush
- sink.close
- end
- def self.print_friendly(test_prefix : String, scores : Hash(String, Hash(Int32, CompareLevel)), update_files : Bool) : Nil
- total_valid = 0
- total_strict = 0
- total_examples = 0
- sink : IO::FileDescriptor
- if update_files
- path = Path[tool_dir, "#{test_prefix}_stats.txt"]
- puts "Updating #{path}"
- sink = File.open(path, "w+")
- else
- sink = STDOUT
- end
- scores.each do |section, map|
- total = map.values.size
- total_examples += total
- section_strict_count = map.values.count { |val| val == CompareLevel::Strict }
- section_loose_count = map.values.count { |val| val == CompareLevel::Loose }
- section_valid_count = section_strict_count + section_loose_count
- total_strict += section_strict_count
- total_valid += section_valid_count
- sink.puts pct(section_valid_count, total, section)
- end
- sink.puts pct(total_valid, total_examples, "TOTAL")
- sink.puts pct(total_strict, total_valid, "TOTAL Strict")
- sink.flush
- sink.close
- end
- def self.main
- section : String? = nil
- raw = false
- update_files = false
- verbose = false
- verbose_loose = false
- flavor : String? = nil
- cli = OptionParser.new do |parser|
- parser.on("--section SECTION", "Restrict tests to one section") { |val| section = val }
- parser.on("--raw", "raw JSON format") { raw = true }
- parser.on("--update-files", "Update stats files in #{tool_dir}") { update_files = true }
- parser.on("--verbose", "Print details for failures and errors") { verbose = true }
- parser.on("--verbose-loose", "Print details for \"loose\" matches") { verbose_loose = true }
- parser.on("--flavor FLAVOR", "") do |_flavor|
- match = @@configs.find { |c| c.prefix == _flavor }
- if match.nil?
- STDERR.puts "FLAVOR must be one of: " + @@configs.map(&.prefix).join(", ")
- exit 64
- end
- flavor = _flavor
- end
- parser.on("-h", "--help", "Show this help message") do
- puts parser
- exit
- end
- parser.missing_option do |op|
- STDERR.puts "Mission value for option #{op}"
- puts parser
- exit 64
- end
- end
- cli.parse
- if update_files && (raw || verbose || (section != nil))
- STDERR.puts "The `update-files` flag must be used by itself"
- puts cli
- exit 64
- end
- test_prefix = flavor
- unless update_files
- test_prefix = @@configs[0].prefix
- end
- test_prefixes = if test_prefix.nil?
- @@configs.map(&.prefix)
- else
- [test_prefix]
- end
- test_prefixes.each do |prefix|
- process_config(prefix, raw, update_files, verbose,
- section, verbose_loose)
- end
- end
- end
- Stats.main
|