class Inspec::Reporters::CLI
Constants
- COLORS
Most currently available Windows terminals have poor support for ANSI extended colors
- INDICATORS
Most currently available Windows terminals have poor support for UTF-8 characters so use these boring indicators
- MULTI_TEST_CONTROL_SUMMARY_MAX_LEN
Public Instance Methods
render()
click to toggle source
# File lib/inspec/reporters/cli.rb, line 43 def render @src_extent_map = {} run_data[:profiles].each do |profile| if profile[:status] == "skipped" platform = run_data[:platform] output("Skipping profile: '#{profile[:name]}' on unsupported platform: '#{platform[:name]}/#{platform[:release]}'.") next end read_control_source(profile) @control_count = 0 output("") print_profile_header(profile) print_standard_control_results(profile) print_anonymous_control_results(profile) if @control_count == 0 output(format_message( indentation: 5, message: "No tests executed." )) end end output("") print_profile_summary print_tests_summary end
Private Instance Methods
all_unique_controls()
click to toggle source
# File lib/inspec/reporters/cli.rb, line 231 def all_unique_controls @unique_controls ||= begin # rubocop:disable Style/RedundantBegin run_data[:profiles].flat_map do |profile| profile[:controls] end.uniq end end
anonymous_controls_from_profile(profile)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 339 def anonymous_controls_from_profile(profile) profile[:controls].select { |c| is_anonymous_control?(c) && !c[:results].nil? } end
format_control_header(control)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 124 def format_control_header(control) impact = control.impact_string format_message( color: impact, indicator: impact, message: control.title_for_report ) end
format_control_source(control)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 133 def format_control_source(control) src = @control_source[control.id] message = "Control Source from #{src[:path]}:#{src[:start]}..#{src[:end]}\n" message += src[:content] format_message( color: "skipped", indentation: 5, message: message ) end
format_message(message_info)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 211 def format_message(message_info) indicator = message_info[:indicator] color = message_info[:color] indentation = message_info.fetch(:indentation, 2) message = message_info[:message] message_to_format = "" message_to_format += "#{INDICATORS[indicator]} " unless indicator.nil? message_to_format += message.to_s.lstrip.force_encoding(Encoding::UTF_8) format_with_color(color, indent_lines(message_to_format, indentation)) end
format_profile_name(profile)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 116 def format_profile_name(profile) if profile[:title].nil? (profile[:name] || "unknown").to_s else "#{profile[:title]} (#{profile[:name] || "unknown"})" end end
format_result(control, result, type)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 189 def format_result(control, result, type) impact = control.impact_string_for_result(result) message = if result[:status] == "skipped" result[:skip_message] elsif type == :anonymous result[:expectation_message] else result[:code_desc] end # append any failure details to the message if they exist message += "\n#{result[:message]}" if result[:message] format_message( color: impact, indicator: impact, indentation: 5, message: message ) end
format_with_color(color_name, text)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 224 def format_with_color(color_name, text) return text if defined?(RSpec.configuration) && !RSpec.configuration.color return text unless COLORS.key?(color_name) "#{COLORS[color_name]}#{text}#{COLORS["reset"]}" end
indent_lines(message, indentation)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 347 def indent_lines(message, indentation) message.lines.map { |line| " " * indentation + line }.join end
is_anonymous_control?(control)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 343 def is_anonymous_control?(control) control[:id].start_with?("(generated from ") end
print_anonymous_control_results(profile)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 103 def print_anonymous_control_results(profile) anonymous_controls_from_profile(profile).each do |control_from_profile| control = Control.new(control_from_profile) next if control.results.nil? output(format_control_header(control)) control.results.each do |result| output(format_result(control, result, :anonymous)) @control_count += 1 end end end
print_profile_header(profile)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 72 def print_profile_header(profile) header = { "Profile" => format_profile_name(profile), "Version" => profile[:version] || "(not specified)", } header["Failure Message"] = profile[:status_message] if profile[:status] == "failed" header["Target"] = run_data[:platform][:target] unless run_data[:platform][:target].nil? header["Target ID"] = @config["target_id"] unless @config["target_id"].nil? pad = header.keys.max_by(&:length).length + 1 header.each do |title, value| output(format("%-#{pad}s %s", title + ":", value)) end output("") end
print_profile_summary()
click to toggle source
# File lib/inspec/reporters/cli.rb, line 295 def print_profile_summary summary = profile_summary return unless summary["total"] > 0 success_str = summary["passed"] == 1 ? "1 successful control" : "#{summary["passed"]} successful controls" failed_str = summary["failed"] == 1 ? "1 control failure" : "#{summary["failed"]} control failures" skipped_str = summary["skipped"] == 1 ? "1 control skipped" : "#{summary["skipped"]} controls skipped" success_color = summary["passed"] > 0 ? "passed" : "no_color" failed_color = summary["failed"] > 0 ? "failed" : "no_color" skipped_color = summary["skipped"] > 0 ? "skipped" : "no_color" s = format( "Profile Summary: %s, %s, %s", format_with_color(success_color, success_str), format_with_color(failed_color, failed_str), format_with_color(skipped_color, skipped_str) ) output(s) if summary["total"] > 0 end
print_standard_control_results(profile)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 88 def print_standard_control_results(profile) standard_controls_from_profile(profile).each do |control_from_profile| control = Control.new(control_from_profile) next if control.results.nil? output(format_control_header(control)) output(format_control_source(control)) if Inspec::Config.cached[:reporter_include_source] control.results.each do |result| output(format_result(control, result, :standard)) @control_count += 1 end end output("") if @control_count > 0 end
print_tests_summary()
click to toggle source
# File lib/inspec/reporters/cli.rb, line 316 def print_tests_summary summary = tests_summary failed_str = summary["failed"] == 1 ? "1 failure" : "#{summary["failed"]} failures" success_color = summary["passed"] > 0 ? "passed" : "no_color" failed_color = summary["failed"] > 0 ? "failed" : "no_color" skipped_color = summary["skipped"] > 0 ? "skipped" : "no_color" s = format( "Test Summary: %s, %s, %s", format_with_color(success_color, "#{summary["passed"]} successful"), format_with_color(failed_color, failed_str), format_with_color(skipped_color, "#{summary["skipped"]} skipped") ) output(s) end
profile_summary()
click to toggle source
# File lib/inspec/reporters/cli.rb, line 239 def profile_summary failed = 0 skipped = 0 passed = 0 all_unique_controls.each do |control| next if control[:id].start_with? "(generated from " next unless control[:results] if control[:results].any? { |r| r[:status] == "failed" } failed += 1 elsif control[:results].any? { |r| r[:status] == "skipped" } skipped += 1 else passed += 1 end end total = failed + passed + skipped { "total" => total, "failed" => failed, "skipped" => skipped, "passed" => passed, } end
read_control_source(profile)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 144 def read_control_source(profile) return unless Inspec::Config.cached[:reporter_include_source] @control_source = {} src_extent_map = {} # First pass: build map of paths => ids => [start] all_unique_controls.each do |control| id = control[:id] path = control[:source_location][:ref] start = control[:source_location][:line] next if path.nil? || start.nil? src_extent_map[path] ||= [] src_extent_map[path] << { start: start, id: id } end # Now sort the controls by their starting line in their control file src_extent_map.values.each do |extent_list| extent_list.sort! { |a, b| a[:start] <=> b[:start] } end # Third pass: Read in files and split into lines src_extent_map.keys.each do |path| control_file_lines = File.read(path).lines # TODO error handling last_line_in_file = control_file_lines.count extent_list = src_extent_map[path] extent_list.each_with_index do |extent, idx| if idx == extent_list.count - 1 # Last entry extent[:end] = last_line_in_file else extent[:end] = extent_list[idx + 1][:start] - 1 end @control_source[extent[:id]] = { path: path, start: extent[:start], end: extent[:end], content: control_file_lines.slice(extent[:start] - 1, extent[:end] - extent[:start] + 1).join(""), } end end end
standard_controls_from_profile(profile)
click to toggle source
# File lib/inspec/reporters/cli.rb, line 335 def standard_controls_from_profile(profile) profile[:controls].reject { |c| is_anonymous_control?(c) } end
tests_summary()
click to toggle source
# File lib/inspec/reporters/cli.rb, line 267 def tests_summary total = 0 failed = 0 skipped = 0 passed = 0 all_unique_controls.each do |control| next unless control[:results] control[:results].each do |result| if result[:status] == "failed" failed += 1 elsif result[:status] == "skipped" skipped += 1 else passed += 1 end end end { "total" => total, "failed" => failed, "skipped" => skipped, "passed" => passed, } end