From af0ef2267699b3fc40ced4ffd00b8a78c30ba4fe Mon Sep 17 00:00:00 2001 From: Mike Karlesky Date: Tue, 30 Apr 2024 16:09:21 -0400 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Centralized=20logging=20he?= =?UTF-8?q?aders=20+=20decorators?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Logging statements now add a default set of headers (only for warnings and errors) with options to add predefined headers for any logging needs - Special header decorators are similarly centrally added to messages (based on decorator status) and also decorators used in strings are removed if decoration disabled - Restored decorators in CLI messages - Fixed some CLI silent handling and oopsie newlines in help headings - Centralized exception reporting in bin/ and lib/ to a single boom_handler() in bin/ - Cleaned up exception handling edge case for `build` CLI task retry - Cleaned up exception logging to all flow through streaminator properly - Modified report generation for test results to adapt verbosity to test failure vs. test success case - Fixed verbosity handling for examples list CLI command --- bin/ceedling | 58 +++++-- bin/cli.rb | 8 +- bin/cli_handler.rb | 60 +++++--- bin/configinator.rb | 4 +- bin/path_validator.rb | 6 +- bin/projectinator.rb | 9 +- lib/ceedling/config_matchinator.rb | 14 +- lib/ceedling/configurator.rb | 6 +- lib/ceedling/configurator_setup.rb | 14 +- lib/ceedling/configurator_validator.rb | 14 +- lib/ceedling/constants.rb | 27 +++- lib/ceedling/file_finder_helper.rb | 8 +- lib/ceedling/generator_helper.rb | 8 +- .../generator_test_results_sanity_checker.rb | 7 +- lib/ceedling/include_pathinator.rb | 8 +- lib/ceedling/plugin_reportinator.rb | 14 +- lib/ceedling/plugin_reportinator_helper.rb | 8 +- lib/ceedling/rakefile.rb | 32 ++-- lib/ceedling/streaminator.rb | 143 +++++++++++++++--- lib/ceedling/tasks_release.rake | 13 +- lib/ceedling/tasks_tests.rake | 8 +- lib/ceedling/test_invoker_helper.rb | 4 +- lib/ceedling/tool_executor.rb | 21 ++- lib/ceedling/tool_executor_helper.rb | 4 +- lib/ceedling/tool_validator.rb | 8 +- plugins/bullseye/lib/bullseye.rb | 4 +- plugins/gcov/gcov.rake | 16 +- plugins/gcov/lib/gcov.rb | 15 +- plugins/gcov/lib/gcovr_reportinator.rb | 8 +- .../gcov/lib/reportgenerator_reportinator.rb | 2 +- 30 files changed, 360 insertions(+), 191 deletions(-) diff --git a/bin/ceedling b/bin/ceedling index 9217b0176..17016e81a 100755 --- a/bin/ceedling +++ b/bin/ceedling @@ -23,6 +23,37 @@ $LOAD_PATH.unshift( CEEDLING_APPCFG[:ceedling_lib_base_path] ) require 'cli' # Located alongside this file in CEEDLING_BIN require 'constructor' # Assumed installed via Ceedling gem dependencies +require 'ceedling/constants' + +# Single exception handler for multiple booms +def bootloader_boom_handler(loginator, exception) + $stderr.puts( "\n" ) + loginator.stream_puts( exception.message, Verbosity::ERRORS, LogLabels::EXCEPTION ) + $stderr.puts( exception.backtrace ) if ( defined?( PROJECT_DEBUG ) and PROJECT_DEBUG ) +end + + +# Centralized exception handler for: +# 1. Bootloader (bin/) +# 2. Application (lib/) last resort / outer exception handling +def boom_handler(streaminator, exception) + $stderr.puts( "\n" ) + + if !streaminator.nil? + streaminator.stream_puts( exception.message, Verbosity::ERRORS, LogLabels::EXCEPTION ) + streaminator.stream_puts( "Backtrace ==>", Verbosity::DEBUG ) + # Output to console the exception backtrace, formatted like Ruby does it + streaminator.stream_puts( "#{exception.backtrace.first}: #{exception.message} (#{exception.class})", Verbosity::DEBUG ) + streaminator.stream_puts( exception.backtrace.drop(1).map{|s| "\t#{s}"}.join("\n"), Verbosity::DEBUG ) + + # Something went really wrong... logging isn't even up and running yet + else + $stderr.puts( "#{exception.class} ==> #{exception.message}" ) + $stderr.puts( "Backtrace ==>" ) + $stderr.puts( exception.backtrace ) + end +end + # Entry point begin @@ -42,6 +73,7 @@ begin require 'diy' bin_objects_filepath = File.join( ceedling_bin_path, 'objects.yml' ) + objects = {} # Empty initial hash to be redefined (fingers crossed) objects = DIY::Context.from_yaml( File.read( bin_objects_filepath ) ) objects.build_everything() @@ -86,17 +118,23 @@ rescue Thor::UndefinedCommandError # and try again by forcing the Thor `build` command at the beginning of the command line. # This way, our Thor handling will process option flags and properly pass the Rake tasks # along as well. - CeedlingTasks::CLI.start( _ARGV.unshift( 'build' ), - { - :app_cfg => CEEDLING_APPCFG, - :objects => objects, - } - ) -# Bootloader boom handling (ideally this never runs... we failed to build much if we're here) -rescue StandardError => e - $stderr.puts( "\nERROR: #{e.message}" ) - $stderr.puts( e.backtrace ) if ( defined?( PROJECT_DEBUG ) and PROJECT_DEBUG ) + # Necessary nested exception handling + begin + CeedlingTasks::CLI.start( _ARGV.unshift( 'build' ), + { + :app_cfg => CEEDLING_APPCFG, + :objects => objects, + } + ) + rescue StandardError => ex + boom_handler( objects[:streaminator], ex ) + exit(1) + end + +# Bootloader boom handling +rescue StandardError => ex + boom_handler( objects[:streaminator], ex ) exit(1) end diff --git a/bin/cli.rb b/bin/cli.rb index e05c708e8..0f49e58ad 100644 --- a/bin/cli.rb +++ b/bin/cli.rb @@ -327,6 +327,7 @@ def environment() end desc "examples", "List available example projects" + method_option :debug, :type => :boolean, :default => false, :hide => true long_desc <<-LONGDESC `ceedling examples` lists the names of the example projects that come packaged with Ceedling. @@ -334,7 +335,12 @@ def environment() to extract an example project to your filesystem. LONGDESC def examples() - @handler.list_examples( ENV, @app_cfg ) + # Get unfrozen copies so we can add / modify + _options = options.dup() + + _options[:verbosity] = options[:debug] ? VERBOSITY_DEBUG : nil + + @handler.list_examples( ENV, @app_cfg, _options ) end diff --git a/bin/cli_handler.rb b/bin/cli_handler.rb index cda3d6b23..319a00519 100644 --- a/bin/cli_handler.rb +++ b/bin/cli_handler.rb @@ -27,18 +27,18 @@ def setup() # Thor application help + Rake help (if available) def app_help(env, app_cfg, options, command, &thor_help) - @helper.set_verbosity( options[:verbosity] ) + verbosity = @helper.set_verbosity( options[:verbosity] ) # If help requested for a command, show it and skip listing build tasks if !command.nil? # Block handler - @streaminator.stream_puts( 'Ceedling Application ' ) + @streaminator.out( 'Ceedling Application ', Verbosity::NORMAL, LogLabels::TITLE ) thor_help.call( command ) if block_given? return end # Display Thor-generated help listing - @streaminator.stream_puts( 'Ceedling Application ' ) + @streaminator.out( 'Ceedling Application ', Verbosity::NORMAL, LogLabels::TITLE ) thor_help.call( command ) if block_given? # If it was help for a specific command, we're done @@ -48,7 +48,14 @@ def app_help(env, app_cfg, options, command, &thor_help) @path_validator.standardize_paths( options[:project], *options[:mixin], ) return if !@projectinator.config_available?( filepath:options[:project], env:env ) - list_rake_tasks( env:env, app_cfg:app_cfg, filepath:options[:project], mixins:options[:mixin] ) + list_rake_tasks( + env:env, + app_cfg: app_cfg, + filepath: options[:project], + mixins: options[:mixin], + # Silent Ceedling loading unless debug verbosity + silent: !(verbosity == Verbosity::DEBUG) + ) end @@ -108,7 +115,8 @@ def new_project(env, app_cfg, options, name, dest) @actions._touch_file( File.join( dest, 'test/support', '.gitkeep') ) end - @streaminator.stream_puts( "\nNew project '#{name}' created at #{dest}/\n" ) + @streaminator.out( "\n" ) + @streaminator.stream_puts( "New project '#{name}' created at #{dest}/\n", Verbosity::NORMAL, LogLabels::TITLE ) end @@ -125,8 +133,8 @@ def upgrade_project(env, app_cfg, options, path) which, _ = @helper.which_ceedling?( env:env, app_cfg:app_cfg ) if (which == :gem) - msg = "NOTICE: Project configuration specifies the Ceedling gem, not vendored Ceedling" - @streaminator.stream_puts( msg, Verbosity::NORMAL ) + msg = "Project configuration specifies the Ceedling gem, not vendored Ceedling" + @streaminator.stream_puts( msg, Verbosity::NORMAL, LogLabels::NOTICE ) end # Thor Actions for project tasks use paths in relation to this path @@ -145,7 +153,8 @@ def upgrade_project(env, app_cfg, options, path) @helper.copy_docs( app_cfg[:ceedling_root_path], path ) end - @streaminator.stream_puts( "\nUpgraded project at #{path}/\n" ) + @streaminator.out( "\n" ) + @streaminator.stream_puts( "Upgraded project at #{path}/\n", Verbosity::NORMAL, LogLabels::TITLE ) end @@ -238,7 +247,9 @@ def dumpconfig(env, app_cfg, options, filepath, sections) end ensure @helper.dump_yaml( config, filepath, sections ) - @streaminator.stream_puts( "\nDumped project configuration to #{filepath}\n" ) + + @streaminator.out( "\n" ) + @streaminator.stream_puts( "Dumped project configuration to #{filepath}\n", Verbosity::NORMAL, LogLabels::TITLE ) end end @@ -277,17 +288,20 @@ def environment(env, app_cfg, options) end end - output = "\nEnvironment variables:\n" + output = "Environment variables:\n" env_list.sort.each do |line| - output << " * #{line}\n" + output << " • #{line}\n" end - @streaminator.stream_puts( output + "\n" ) + @streaminator.out( "\n" ) + @streaminator.stream_puts( output + "\n", Verbosity::NORMAL, LogLabels::TITLE ) end - def list_examples(env, app_cfg) + def list_examples(env, app_cfg, options) + @helper.set_verbosity( options[:verbosity] ) + # Process which_ceedling for app_cfg modifications but ignore return values @helper.which_ceedling?( env:env, app_cfg:app_cfg ) @@ -295,11 +309,12 @@ def list_examples(env, app_cfg) raise( "No examples projects found") if examples.empty? - output = "\nAvailable example projects:\n" + output = "Available example projects:\n" - examples.each {|example| output << " * #{example}\n" } + examples.each {|example| output << " • #{example}\n" } - @streaminator.stream_puts( output + "\n" ) + @streaminator.out( "\n" ) + @streaminator.stream_puts( output + "\n", Verbosity::NORMAL, LogLabels::TITLE ) end @@ -338,7 +353,8 @@ def create_example(env, app_cfg, options, name, dest) # Copy in documentation @helper.copy_docs( app_cfg[:ceedling_root_path], dest ) if options[:docs] - @streaminator.stream_puts( "\nExample project '#{name}' created at #{dest}/\n" ) + @streaminator.out( "\n" ) + @streaminator.stream_puts( "Example project '#{name}' created at #{dest}/\n", Verbosity::NORMAL, LogLabels::TITLE ) end @@ -350,7 +366,7 @@ def version() Unity => #{Ceedling::Version::UNITY} CException => #{Ceedling::Version::CEXCEPTION} VERSION - @streaminator.stream_puts( version ) + @streaminator.stream_puts( version, Verbosity::NORMAL, LogLabels::TITLE ) end @@ -358,13 +374,14 @@ def version() private - def list_rake_tasks(env:, app_cfg:, filepath:nil, mixins:[]) + def list_rake_tasks(env:, app_cfg:, filepath:nil, mixins:[], silent:false) _, config = @configinator.loadinate( builtin_mixins:BUILTIN_MIXINS, filepath: filepath, mixins: mixins, - env: env + env: env, + silent: silent ) # Save reference to loaded configuration @@ -378,7 +395,8 @@ def list_rake_tasks(env:, app_cfg:, filepath:nil, mixins:[]) default_tasks: app_cfg[:default_tasks] ) - @streaminator.stream_puts( "Ceedling Build & Plugin Tasks:\n(Parameterized tasks tend to need enclosing quotes or escape sequences in most shells)" ) + msg = "Ceedling Build & Plugin Tasks:\n(Parameterized tasks tend to need enclosing quotes or escape sequences in most shells)" + @streaminator.stream_puts( msg, Verbosity::NORMAL, LogLabels::TITLE ) @helper.print_rake_tasks() end diff --git a/bin/configinator.rb b/bin/configinator.rb index 881e2cc6f..b99aa86f5 100644 --- a/bin/configinator.rb +++ b/bin/configinator.rb @@ -11,13 +11,13 @@ class Configinator constructor :config_walkinator, :projectinator, :mixinator - def loadinate(builtin_mixins:, filepath:nil, mixins:[], env:{}) + def loadinate(builtin_mixins:, filepath:nil, mixins:[], env:{}, silent:false) # Aliases for clarity cmdline_filepath = filepath cmdline_mixins = mixins || [] # Load raw config from command line, environment variable, or default filepath - project_filepath, config = @projectinator.load( filepath:cmdline_filepath, env:env ) + project_filepath, config = @projectinator.load( filepath:cmdline_filepath, env:env, silent:silent ) # Extract cfg_enabled_mixins mixins list plus load paths list from config cfg_enabled_mixins, cfg_load_paths = @projectinator.extract_mixins( config: config ) diff --git a/bin/path_validator.rb b/bin/path_validator.rb index d3f534b2b..3133024f4 100644 --- a/bin/path_validator.rb +++ b/bin/path_validator.rb @@ -16,20 +16,20 @@ def validate(paths:, source:, type: :filepath) # Error out on empty paths if path.empty? validated = false - @streaminator.stream_puts( "ERROR: #{source} contains an empty path", Verbosity::ERRORS ) + @streaminator.stream_puts( "#{source} contains an empty path", Verbosity::ERRORS ) next end # Error out if path is not a directory / does not exist if (type == :directory) and !@file_wrapper.directory?( path ) validated = false - @streaminator.stream_puts( "ERROR: #{source} '#{path}' does not exist as a directory in the filesystem", Verbosity::ERRORS ) + @streaminator.stream_puts( "#{source} '#{path}' does not exist as a directory in the filesystem", Verbosity::ERRORS ) end # Error out if filepath does not exist if (type == :filepath) and !@file_wrapper.exist?( path ) validated = false - @streaminator.stream_puts( "ERROR: #{source} '#{path}' does not exist in the filesystem", Verbosity::ERRORS ) + @streaminator.stream_puts( "#{source} '#{path}' does not exist in the filesystem", Verbosity::ERRORS ) end end diff --git a/bin/projectinator.rb b/bin/projectinator.rb index 23cfbbc5b..bf89db594 100644 --- a/bin/projectinator.rb +++ b/bin/projectinator.rb @@ -131,7 +131,7 @@ def validate_mixins(mixins:, load_paths:, builtins:, source:, yaml_extension:) # Validate mixin filepaths if @path_validator.filepath?( mixin ) if !@file_wrapper.exist?( mixin ) - @streaminator.stream_puts( "ERROR: Cannot find mixin at #{mixin}" ) + @streaminator.stream_puts( "Cannot find mixin at #{mixin}", Verbosity::ERRORS ) validated = false end @@ -148,7 +148,7 @@ def validate_mixins(mixins:, load_paths:, builtins:, source:, yaml_extension:) builtins.keys.each {|key| found = true if (mixin == key.to_s)} if !found - msg = "ERROR: #{source} '#{mixin}' cannot be found in mixin load paths as '#{mixin + yaml_extension}' or among built-in mixins" + msg = "#{source} '#{mixin}' cannot be found in mixin load paths as '#{mixin + yaml_extension}' or among built-in mixins" @streaminator.stream_puts( msg, Verbosity::ERRORS ) validated = false end @@ -206,7 +206,10 @@ def load_filepath(filepath, method, silent) config = {} if config.nil? # Log what the heck we loaded - @streaminator.stream_puts( "Loaded #{'(empty) ' if config.empty?}project configuration #{method} using #{filepath}" ) if !silent + if !silent + msg = "Loaded #{'(empty) ' if config.empty?}project configuration #{method} using #{filepath}" + @streaminator.stream_puts( msg, Verbosity::NORMAL, LogLabels::TITLE ) + end return config rescue Errno::ENOENT diff --git a/lib/ceedling/config_matchinator.rb b/lib/ceedling/config_matchinator.rb index e9d8fb5cc..d65920991 100644 --- a/lib/ceedling/config_matchinator.rb +++ b/lib/ceedling/config_matchinator.rb @@ -62,8 +62,8 @@ def get_config(primary:, secondary:, tertiary:nil) return elem if tertiary.nil? # Otherwise, if an tertiary is specified but we have an array, go boom - error = "ERROR: :#{primary} ↳ :#{secondary} present in project configuration but does not contain :#{tertiary}." - raise CeedlingException.new(error) + error = ":#{primary} ↳ :#{secondary} present in project configuration but does not contain :#{tertiary}." + raise CeedlingException.new( error ) # If [primary][secondary] is a hash elsif elem.class == Hash @@ -81,8 +81,8 @@ def get_config(primary:, secondary:, tertiary:nil) # If [primary][secondary] is nothing we expect--something other than an array or hash else - error = "ERROR: :#{primary} ↳ :#{secondary} in project configuration is neither a list nor hash." - raise CeedlingException.new(error) + error = ":#{primary} ↳ :#{secondary} in project configuration is neither a list nor hash." + raise CeedlingException.new( error ) end return nil @@ -93,8 +93,8 @@ def validate_matchers(hash:, section:, context:, operation:nil) hash.each do |k, v| if v == nil path = generate_matcher_path( section, context, operation ) - error = "ERROR: Missing list of values for #{path} ↳ '#{k}' matcher in project configuration." - raise CeedlingException.new(error) + error = "Missing list of values for #{path} ↳ '#{k}' matcher in project configuration." + raise CeedlingException.new( error ) end end end @@ -106,7 +106,7 @@ def matches?(hash:, filepath:, section:, context:, operation:nil) # Sanity check if filepath.nil? path = generate_matcher_path(section, context, operation) - error = "ERROR: #{path} ↳ #{matcher} matching provided nil #{filepath}" + error = "#{path} ↳ #{matcher} matching provided nil #{filepath}" raise CeedlingException.new(error) end diff --git a/lib/ceedling/configurator.rb b/lib/ceedling/configurator.rb index d41ce190f..18462aeba 100644 --- a/lib/ceedling/configurator.rb +++ b/lib/ceedling/configurator.rb @@ -160,7 +160,7 @@ def tools_setup(config) tool = config[:tools][name] if not tool.is_a?(Hash) - raise CeedlingException.new("ERROR: Expected configuration for tool :#{name} is a Hash but found #{tool.class}") + raise CeedlingException.new( "Expected configuration for tool :#{name} is a Hash but found #{tool.class}" ) end # Populate name if not given @@ -356,7 +356,7 @@ def validate_essential(config) blotter &= @configurator_setup.validate_required_section_values( config ) if !blotter - raise CeedlingException.new("ERROR: Ceedling configuration failed validation") + raise CeedlingException.new("Ceedling configuration failed validation") end end @@ -370,7 +370,7 @@ def validate_final(config) blotter &= @configurator_setup.validate_plugins( config ) if !blotter - raise CeedlingException.new("ERROR: Ceedling configuration failed validation") + raise CeedlingException.new( "Ceedling configuration failed validation" ) end end diff --git a/lib/ceedling/configurator_setup.rb b/lib/ceedling/configurator_setup.rb index 92af46289..25f4bdbd6 100644 --- a/lib/ceedling/configurator_setup.rb +++ b/lib/ceedling/configurator_setup.rb @@ -176,32 +176,32 @@ def validate_threads(config) case compile_threads when Integer if compile_threads < 1 - @streaminator.stream_puts("ERROR: :project ↳ :compile_threads must be greater than 0", Verbosity::ERRORS) + @streaminator.stream_puts( ":project ↳ :compile_threads must be greater than 0", Verbosity::ERRORS ) valid = false end when Symbol if compile_threads != :auto - @streaminator.stream_puts("ERROR: :project ↳ :compile_threads is neither an integer nor :auto", Verbosity::ERRORS) + @streaminator.stream_puts( ":project ↳ :compile_threads is neither an integer nor :auto", Verbosity::ERRORS ) valid = false end else - @streaminator.stream_puts("ERROR: :project ↳ :compile_threads is neither an integer nor :auto", Verbosity::ERRORS) + @streaminator.stream_puts( ":project ↳ :compile_threads is neither an integer nor :auto", Verbosity::ERRORS ) valid = false end case test_threads when Integer if test_threads < 1 - @streaminator.stream_puts("ERROR: :project ↳ :test_threads must be greater than 0", Verbosity::ERRORS) + @streaminator.stream_puts( ":project ↳ :test_threads must be greater than 0", Verbosity::ERRORS ) valid = false end when Symbol if test_threads != :auto - @streaminator.stream_puts("ERROR: :project ↳ :test_threads is neither an integer nor :auto", Verbosity::ERRORS) + @streaminator.stream_puts( ":project ↳ :test_threads is neither an integer nor :auto", Verbosity::ERRORS ) valid = false end else - @streaminator.stream_puts("ERROR: :project ↳ :test_threads is neither an integer nor :auto", Verbosity::ERRORS) + @streaminator.stream_puts( ":project ↳ :test_threads is neither an integer nor :auto", Verbosity::ERRORS ) valid = false end @@ -215,7 +215,7 @@ def validate_plugins(config) Set.new( @configurator_plugins.programmatic_plugins ) missing_plugins.each do |plugin| - @streaminator.stream_puts("ERROR: Plugin '#{plugin}' not found in built-in or project Ruby load paths. Check load paths and plugin naming and path conventions.", Verbosity::ERRORS) + @streaminator.stream_puts("Plugin '#{plugin}' not found in built-in or project Ruby load paths. Check load paths and plugin naming and path conventions.", Verbosity::ERRORS) end return ( (missing_plugins.size > 0) ? false : true ) diff --git a/lib/ceedling/configurator_validator.rb b/lib/ceedling/configurator_validator.rb index a277cf321..d96df945d 100644 --- a/lib/ceedling/configurator_validator.rb +++ b/lib/ceedling/configurator_validator.rb @@ -22,7 +22,7 @@ def exists?(config, *keys) if (not exist) walk = @reportinator.generate_config_walk( keys ) - @streaminator.stream_puts("ERROR: Required config file entry #{walk} does not exist.", Verbosity::ERRORS ) + @streaminator.stream_puts( "Required config file entry #{walk} does not exist.", Verbosity::ERRORS ) end return exist @@ -47,7 +47,7 @@ def validate_path_list(config, *keys) # If (partial) path does not exist, complain if (not @file_wrapper.exist?( _path )) walk = @reportinator.generate_config_walk( keys, hash[:depth] ) - @streaminator.stream_puts("ERROR: Config path #{walk} => '#{_path}' does not exist in the filesystem.", Verbosity::ERRORS ) + @streaminator.stream_puts( "Config path #{walk} => '#{_path}' does not exist in the filesystem.", Verbosity::ERRORS ) exist = false end end @@ -76,7 +76,7 @@ def validate_paths_entries(config, key) if @file_wrapper.exist?( _path ) and !@file_wrapper.directory?( _path ) # Path is a simple filepath (not a directory) - warning = "WARNING: #{walk} => '#{_path}' is a filepath and will be ignored (FYI :paths is directory-oriented while :files is file-oriented)" + warning = "#{walk} => '#{_path}' is a filepath and will be ignored (FYI :paths is directory-oriented while :files is file-oriented)" @streaminator.stream_puts( warning, Verbosity::COMPLAIN ) next # Skip to next path @@ -98,7 +98,7 @@ def validate_paths_entries(config, key) # Path did not work -- must be malformed glob or glob referencing path that does not exist. # (An earlier step validates all simple directory paths). if dirs.empty? - error = "ERROR: #{walk} => '#{_path}' yielded no directories -- matching glob is malformed or directories do not exist" + error = "#{walk} => '#{_path}' yielded no directories -- matching glob is malformed or directories do not exist" @streaminator.stream_puts( error, Verbosity::ERRORS ) valid = false end @@ -126,7 +126,7 @@ def validate_files_entries(config, key) if @file_wrapper.exist?( _path ) and @file_wrapper.directory?( _path ) # Path is a simple directory path (and is naturally ignored by FileList without a glob pattern) - warning = "WARNING: #{walk} => '#{_path}' is a directory path and will be ignored (FYI :files is file-oriented while :paths is directory-oriented)" + warning = "#{walk} => '#{_path}' is a directory path and will be ignored (FYI :files is file-oriented while :paths is directory-oriented)" @streaminator.stream_puts( warning, Verbosity::COMPLAIN ) next # Skip to next path @@ -136,7 +136,7 @@ def validate_files_entries(config, key) # If file list is empty, complain if (filelist.size == 0) - error = "#{walk} => 'ERROR: #{_path}' yielded no files -- matching glob is malformed or files do not exist" + error = "#{walk} => '#{_path}' yielded no files -- matching glob is malformed or files do not exist" @streaminator.stream_puts( error, Verbosity::ERRORS ) valid = false end @@ -152,7 +152,7 @@ def validate_filepath_simple(path, *keys) if (not @file_wrapper.exist?(validate_path)) walk = @reportinator.generate_config_walk( keys, keys.size ) - @streaminator.stream_puts("ERROR: Config path '#{validate_path}' associated with #{walk} does not exist in the filesystem.", Verbosity::ERRORS ) + @streaminator.stream_puts("Config path '#{validate_path}' associated with #{walk} does not exist in the filesystem.", Verbosity::ERRORS ) return false end diff --git a/lib/ceedling/constants.rb b/lib/ceedling/constants.rb index 1e4390153..c9d547352 100644 --- a/lib/ceedling/constants.rb +++ b/lib/ceedling/constants.rb @@ -6,12 +6,12 @@ # ========================================================================= class Verbosity - SILENT = 0 # as silent as possible (though there are some messages that must be spit out) - ERRORS = 1 # only errors - COMPLAIN = 2 # spit out errors and warnings/notices - NORMAL = 3 # errors, warnings/notices, standard status messages - OBNOXIOUS = 4 # all messages including extra verbose output (used for lite debugging / verification) - DEBUG = 5 # special extra verbose output for hardcore debugging + SILENT = 0 # As silent as possible (though there are some messages that must be spit out) + ERRORS = 1 # Only errors + COMPLAIN = 2 # Spit out errors and warnings/notices + NORMAL = 3 # Errors, warnings/notices, standard status messages + OBNOXIOUS = 4 # All messages including extra verbose output (used for lite debugging / verification) + DEBUG = 5 # Special extra verbose output for hardcore debugging end VERBOSITY_OPTIONS = { @@ -23,6 +23,21 @@ class Verbosity :debug => Verbosity::DEBUG, }.freeze() +class LogLabels + NONE = 0 # Override logic and settings with no label and no decoration + AUTO = 1 # Default labeling and decorators + NOTICE = 2 # 'NOTICE:' + WARNING = 3 # 'WARNING:' + ERROR = 4 # 'ERROR:' + EXCEPTION = 5 # 'EXCEPTION:' + SEGFAULT = 6 # 'SEGFAULT:' + TITLE = 7 # Seedling decorator only + + # Verbosity levels ERRORS, COMPLAIN, and NORMAL default to certain labels or lack thereof + # The above label constarts are available to override Loginator's default AUTO level as needed + # See Loginator comments +end + class DurationCounts DAY_MS = (24 * 60 * 60 * 1000) HOUR_MS = (60 * 60 * 1000) diff --git a/lib/ceedling/file_finder_helper.rb b/lib/ceedling/file_finder_helper.rb index 0f8fd8ab4..57a028a5e 100644 --- a/lib/ceedling/file_finder_helper.rb +++ b/lib/ceedling/file_finder_helper.rb @@ -88,13 +88,13 @@ def handle_missing_file(filename, complain) private def blow_up(filename, extra_message="") - error = ["ERROR: Found no file `#{filename}` in search paths.", extra_message].join(' ').strip - raise CeedlingException.new(error) + error = ["Found no file `#{filename}` in search paths.", extra_message].join(' ').strip + raise CeedlingException.new( error ) end def gripe(filename, extra_message="") - warning = ["WARNING: Found no file `#{filename}` in search paths.", extra_message].join(' ').strip - @streaminator.stream_puts(warning + extra_message, Verbosity::COMPLAIN) + warning = ["Found no file `#{filename}` in search paths.", extra_message].join(' ').strip + @streaminator.stream_puts( warning + extra_message, Verbosity::COMPLAIN ) end end diff --git a/lib/ceedling/generator_helper.rb b/lib/ceedling/generator_helper.rb index fcad7f138..c622307e7 100644 --- a/lib/ceedling/generator_helper.rb +++ b/lib/ceedling/generator_helper.rb @@ -21,14 +21,12 @@ def test_results_error_handler(executable, shell_result) if (shell_result[:output].nil? or shell_result[:output].strip.empty?) error = true # mirror style of generic tool_executor failure output - notice = "\n" + - "ERROR: Test executable \"#{File.basename(executable)}\" failed.\n" + + notice = "Test executable \"#{File.basename(executable)}\" failed.\n" + "> Produced no output to $stdout.\n" elsif ((shell_result[:output] =~ TEST_STDOUT_STATISTICS_PATTERN).nil?) error = true # mirror style of generic tool_executor failure output - notice = "\n" + - "ERROR: Test executable \"#{File.basename(executable)}\" failed.\n" + + notice = "Test executable \"#{File.basename(executable)}\" failed.\n" + "> Produced no final test result counts in $stdout:\n" + "#{shell_result[:output].strip}\n" end @@ -40,7 +38,7 @@ def test_results_error_handler(executable, shell_result) notice += "> This is often a symptom of a bad memory access in source or test code.\n\n" - raise CeedlingException.new(notice) + raise CeedlingException.new( notice ) end end diff --git a/lib/ceedling/generator_test_results_sanity_checker.rb b/lib/ceedling/generator_test_results_sanity_checker.rb index d1e21257d..050de1254 100644 --- a/lib/ceedling/generator_test_results_sanity_checker.rb +++ b/lib/ceedling/generator_test_results_sanity_checker.rb @@ -19,7 +19,7 @@ def verify(results, unity_exit_code) # do no sanity checking if it's disabled return if (@configurator.sanity_checks == TestResultsSanityChecks::NONE) - raise CeedlingException.new("ERROR: Test results nil or empty") if results.nil? || results.empty? + raise CeedlingException.new( "Test results nil or empty" ) if results.nil? || results.empty? ceedling_ignores_count = results[:ignores].size ceedling_failures_count = results[:failures].size @@ -56,15 +56,14 @@ def verify(results, unity_exit_code) def sanity_check_warning(file, message) unless defined?(CEEDLING_IGNORE_SANITY_CHECK) - notice = "\n" + - "ERROR: Internal sanity check for test fixture '#{file.ext(@configurator.extension_executable)}' finds that #{message}\n" + + notice = "Internal sanity check for test fixture '#{file.ext(@configurator.extension_executable)}' finds that #{message}\n" + " Possible causes:\n" + " 1. Your test + source dereferenced a null pointer.\n" + " 2. Your test + source indexed past the end of a buffer.\n" + " 3. Your test + source committed a memory access violation.\n" + " 4. Your test fixture produced an exit code of 0 despite execution ending prematurely.\n" + " Sanity check failures of test results are usually a symptom of interrupted test execution.\n\n" - raise CeedlingException.new(notice) + raise CeedlingException.new( notice ) end end diff --git a/lib/ceedling/include_pathinator.rb b/lib/ceedling/include_pathinator.rb index 257576043..5a142795e 100644 --- a/lib/ceedling/include_pathinator.rb +++ b/lib/ceedling/include_pathinator.rb @@ -28,8 +28,8 @@ def validate_test_build_directive_paths # TODO: When Ceedling's base project path handling is resolved, enable this path redefinition # path = File.join( @base_path, path ) unless @file_wrapper.exist?(path) - error = "ERROR: '#{path}' specified by #{UNITY_TEST_INCLUDE_PATH}() within #{test_filepath} not found" - raise CeedlingException.new(error) + error = "'#{path}' specified by #{UNITY_TEST_INCLUDE_PATH}() within #{test_filepath} not found" + raise CeedlingException.new( error ) end end end @@ -50,10 +50,10 @@ def validate_header_files_collection headers.uniq! if headers.length == 0 - error = "WARNING: No header files found in project.\n" + + error = "No header files found in project.\n" + "Add search paths to :paths ↳ :include in your project file and/or use #{UNITY_TEST_INCLUDE_PATH}() in your test files.\n" + "Verify header files with `ceedling paths:include` and\\or `ceedling files:include`." - @streaminator.stream_puts(error, Verbosity::COMPLAIN) + @streaminator.stream_puts( error, Verbosity::COMPLAIN ) end return headers diff --git a/lib/ceedling/plugin_reportinator.rb b/lib/ceedling/plugin_reportinator.rb index 76c1cfb73..31b332c46 100644 --- a/lib/ceedling/plugin_reportinator.rb +++ b/lib/ceedling/plugin_reportinator.rb @@ -50,24 +50,26 @@ def assemble_test_results(results_list, options={:boom => false}) def run_test_results_report(hash, verbosity=Verbosity::NORMAL, &block) if @test_results_template.nil? - raise CeedlingException.new("No test results report template has been set.") + raise CeedlingException.new( "No test results report template has been set." ) end - run_report( $stdout, - @test_results_template, + run_report( @test_results_template, hash, verbosity, &block ) end - def run_report(stream, template, hash=nil, verbosity=Verbosity::NORMAL) + def run_report(template, hash=nil, verbosity=Verbosity::NORMAL) failure = nil failure = yield() if block_given? @plugin_manager.register_build_failure( failure ) - - @plugin_reportinator_helper.run_report( stream, template, hash, verbosity ) + + # Set verbosity to error level if there were failures + verbosity = failure ? Verbosity::ERRORS : Verbosity::NORMAL + + @plugin_reportinator_helper.run_report( template, hash, verbosity ) end # diff --git a/lib/ceedling/plugin_reportinator_helper.rb b/lib/ceedling/plugin_reportinator_helper.rb index 450592fd3..095abfab7 100644 --- a/lib/ceedling/plugin_reportinator_helper.rb +++ b/lib/ceedling/plugin_reportinator_helper.rb @@ -74,9 +74,11 @@ def process_results(aggregate, results) aggregate[:total_time] += results[:time] end - def run_report(stream, template, hash, verbosity) - output = ERB.new(template, trim_mode: "%<>") - @streaminator.stream_puts(output.result(binding()), verbosity, stream) + def run_report(template, hash, verbosity) + output = ERB.new( template, trim_mode: "%<>" ) + + # Run the report template and log result with no log level heading + @streaminator.stream_puts( output.result(binding()), verbosity, LogLabels::NONE ) end end diff --git a/lib/ceedling/rakefile.rb b/lib/ceedling/rakefile.rb index acb813a83..d81c3fea7 100644 --- a/lib/ceedling/rakefile.rb +++ b/lib/ceedling/rakefile.rb @@ -29,24 +29,8 @@ def log_runtime(run, start_time_s, end_time_s, enabled) return if duration.empty? - @ceedling[:streaminator].stream_puts( "\nCeedling #{run} completed in #{duration}" ) -end - -# Centralized last resort, outer exception handling -def boom_handler(exception:, debug:) - if !@ceedling.nil? && !@ceedling[:streaminator].nil? - @ceedling[:streaminator].stream_puts("#{exception.class} ==> #{exception.message}", Verbosity::ERRORS) - if debug - @ceedling[:streaminator].stream_puts("Backtrace ==>", Verbosity::ERRORS) - @ceedling[:streaminator].stream_puts(exception.backtrace, Verbosity::ERRORS) - end - else - # something went really wrong... streaming isn't even up and running yet - $stderr.puts("#{exception.class} ==> #{exception.message}") - $stderr.puts("Backtrace ==>") - $stderr.puts(exception.backtrace) - end - exit(1) + @ceedling[:streaminator].out( "\n" ) + @ceedling[:streaminator].stream_puts( "Ceedling #{run} completed in #{duration}", Verbosity::NORMAL, LogLabels::TITLE ) end start_time = nil # Outside scope of exception handling @@ -62,6 +46,7 @@ def boom_handler(exception:, debug:) # 3. Remove full path from $LOAD_PATH $LOAD_PATH.unshift( CEEDLING_APPCFG[:ceedling_lib_path] ) objects_filepath = File.join( CEEDLING_APPCFG[:ceedling_lib_path], 'objects.yml' ) + @ceedling = {} # Empty hash to be redefined if all goes well @ceedling = DIY::Context.from_yaml( File.read( objects_filepath ) ) @ceedling.build_everything() $LOAD_PATH.delete( CEEDLING_APPCFG[:ceedling_lib_path] ) @@ -100,8 +85,9 @@ def boom_handler(exception:, debug:) # load rakefile component files (*.rake) PROJECT_RAKEFILE_COMPONENT_FILES.each { |component| load(component) } -rescue StandardError => e - boom_handler( exception:e, debug:(defined?(PROJECT_DEBUG) && PROJECT_DEBUG) ) +rescue StandardError => ex + boom_handler( @ceedling[:streaminator], ex ) + exit(1) end def test_failures_handler() @@ -132,17 +118,17 @@ def test_failures_handler() rescue => ex ops_done = SystemWrapper.time_stopwatch_s() log_runtime( 'operations', start_time, ops_done, CEEDLING_APPCFG[:stopwatch] ) - boom_handler( exception:ex, debug:(defined?(PROJECT_DEBUG) && PROJECT_DEBUG) ) + boom_handler( @ceedling[:streaminator], ex ) exit(1) end exit(0) else - @ceedling[:streaminator].stream_puts("\nERROR: Ceedling could not complete operations because of errors.", Verbosity::ERRORS) + @ceedling[:streaminator].stream_puts( "Ceedling could not complete operations because of errors", Verbosity::ERRORS ) begin @ceedling[:plugin_manager].post_error rescue => ex - boom_handler( exception:ex, debug:(defined?(PROJECT_DEBUG) && PROJECT_DEBUG) ) + boom_handler( @ceedling[:streaminator], ex) ensure exit(1) end diff --git a/lib/ceedling/streaminator.rb b/lib/ceedling/streaminator.rb index 975f81ae1..c906c4ffa 100644 --- a/lib/ceedling/streaminator.rb +++ b/lib/ceedling/streaminator.rb @@ -7,7 +7,7 @@ require 'ceedling/constants' -# Streaminator is a convenience object for handling verbosity and writing to the std streams +# Loginator handles console and file output of logging statements class Streaminator @@ -15,43 +15,146 @@ class Streaminator def setup() $decorate = false if $decorate.nil? + + @replace = { + # Problematic characters pattern => Simple characters + /↳/ => '>>', # Config sub-entry notation + /•/ => '*', # Bulleted lists + } + + end + + + # stream_puts() + out() + # ----- + # stream_puts() -> add "\n" + # out() -> raw string to stream(s) + # + # Write the given string to an optional log file and to the console + # - Logging statements to a file are always at the highest verbosity + # - Console logging is controlled by the verbosity level + # + # For default label of LogLabels::AUTO + # - If verbosity ERRORS, add ERROR: heading + # - If verbosity COMPLAIN, added WARNING: heading + # - All other verbosity levels default to no heading + # + # By setting a label: + # - A heading begins a message regardless of verbosity level, except NONE + # - NONE forcibly presents headings and emoji decorators + # + # If decoration is enabled: + # - Add fun emojis before all headings, except TITLE + # - TITLE log level adds seedling emoji alone + # + # If decoration is disabled: + # - No emojis are added to label + # - Any problematic console characters in a message are replaced with + # simpler variants + + def stream_puts(string, verbosity=Verbosity::NORMAL, label=LogLabels::AUTO, stream=nil) + # Call out() with string contatenated with "\n" (unless it aready ends with a newline) + string += "\n" unless string.end_with?( "\n" ) + out( string, verbosity, label, stream ) + end + + + def out(string, verbosity=Verbosity::NORMAL, label=LogLabels::AUTO, stream=nil) + # Choose appropriate console stream + stream = get_stream( verbosity, stream ) + + # Add labels and fun characters + string = format( string, verbosity, label ) + + # write to log as though Verbosity::DEBUG (no filtering at all) + @loginator.log( string, @streaminator_helper.extract_name( stream ) ) + + # Only output when message reaches current verbosity level + return if !(@verbosinator.should_output?( verbosity )) + + # Write to output stream after optionally removing any problematic characters + stream.print( sanitize( string) ) end - def stream_puts(string, verbosity=Verbosity::NORMAL, stream=nil) + def decorate(d) + $decorate = d + end + + ### Private ### + + private + + def get_stream(verbosity, stream) # If no stream has been specified, choose one based on the verbosity level of the prompt if stream.nil? if verbosity <= Verbosity::ERRORS - stream = $stderr + return $stderr else - stream = $stdout + return $stdout end end - # write to log as though Verbosity::OBNOXIOUS - @loginator.log( string, @streaminator_helper.extract_name(stream) ) + return stream + end - # Only stream when message reaches current verbosity level - if (@verbosinator.should_output?(verbosity)) + def format(string, verbosity, label) + prepend = '' - # Apply decorations if supported - if ($decorate) - { - / ↳ / => ' >> ', - /^Ceedling/ => '🌱 Ceedling', - }.each_pair {|k,v| string.gsub!(k,v) } - if (verbosity == Verbosity::ERRORS) - string.sub!(/^\n?/, "\n🪲 ") + # Force no automatic label / decorator + return string if label == LogLabels::NONE + + # Add decorators if enabled + if $decorate + case label + when LogLabels::AUTO + if verbosity == Verbosity::ERRORS + prepend = '🪲 ' + elsif verbosity == Verbosity::COMPLAIN + prepend = '⚠️ ' end + # Otherwise, no decorators for verbosity levels + when LogLabels::NOTICE + prepend = 'ℹ️ ' + when LogLabels::WARNING + prepend = '⚠️ ' + when LogLabels::ERROR + prepend = '🪲 ' + when LogLabels::EXCEPTION + prepend = '🧨 ' + when LogLabels::SEGFAULT + prepend = '☠️ ' + when LogLabels::TITLE + prepend = '🌱 ' end + end - # Write to output stream - stream.puts(string) + # Add headings + case label + when LogLabels::AUTO + if verbosity == Verbosity::ERRORS + prepend += 'ERROR: ' + elsif verbosity == Verbosity::COMPLAIN + prepend += 'WARNING: ' + end + # Otherwise, no headings + when LogLabels::NOTICE + prepend += 'NOTICE: ' + when LogLabels::WARNING + prepend += 'WARNING: ' + when LogLabels::ERROR + prepend += 'ERROR: ' + when LogLabels::EXCEPTION + prepend += 'EXCEPTION: ' end + + return prepend + string end - def decorate(d) - $decorate = d + def sanitize(string) + # Remove problematic console characters in-place if decoration disabled + @replace.each_pair {|k,v| string.gsub!( k, v) } if ($decorate == false) + return string end end diff --git a/lib/ceedling/tasks_release.rake b/lib/ceedling/tasks_release.rake index 438820988..7c16e1ffe 100644 --- a/lib/ceedling/tasks_release.rake +++ b/lib/ceedling/tasks_release.rake @@ -27,17 +27,16 @@ task RELEASE_SYM => [:prepare] do file( PROJECT_RELEASE_BUILD_TARGET => (core_objects + extra_objects + library_objects) ) Rake::Task[PROJECT_RELEASE_BUILD_TARGET].invoke() - rescue StandardError => e + rescue StandardError => ex @ceedling[:application].register_build_failure - @ceedling[:streaminator].stream_puts("#{e.class} ==> #{e.message}", Verbosity::ERRORS) + @ceedling[:streaminator].stream_puts( "#{ex.class} ==> #{ex.message}", Verbosity::ERRORS, LogLabels::EXCEPTION ) # Debug backtrace - @ceedling[:streaminator].stream_puts("Backtrace ==>", Verbosity::DEBUG) - if @ceedling[:verbosinator].should_output?(Verbosity::DEBUG) - @ceedling[:streaminator].stream_puts(e.backtrace, Verbosity::DEBUG) # Formats properly when directly passed to puts() - end - + @ceedling[:streaminator].stream_puts( "Backtrace ==>", Verbosity::DEBUG ) + # Output to console the exception backtrace, formatted like Ruby does it + streaminator.stream_puts( "#{ex.backtrace.first}: #{ex.message} (#{ex.class})", Verbosity::DEBUG ) + streaminator.stream_puts( ex.backtrace.drop(1).map{|s| "\t#{s}"}.join("\n"), Verbosity::DEBUG ) ensure @ceedling[:plugin_manager].post_release end diff --git a/lib/ceedling/tasks_tests.rake b/lib/ceedling/tasks_tests.rake index fa14a1df9..06e4585cc 100644 --- a/lib/ceedling/tasks_tests.rake +++ b/lib/ceedling/tasks_tests.rake @@ -29,9 +29,9 @@ namespace TEST_SYM do desc "Run single test ([*] test or source file name, no path)." task :* do - message = "\nERROR: Oops! '#{TEST_ROOT_NAME}:*' isn't a real task. " + + message = "Oops! '#{TEST_ROOT_NAME}:*' isn't a real task. " + "Use a real test or source file name (no path) in place of the wildcard.\n" + - "Example: rake #{TEST_ROOT_NAME}:foo.c\n\n" + "Example: `ceedling #{TEST_ROOT_NAME}:foo.c`" @ceedling[:streaminator].stream_puts( message, Verbosity::ERRORS ) end @@ -50,7 +50,7 @@ namespace TEST_SYM do if (matches.size > 0) @ceedling[:test_invoker].setup_and_invoke(tests:matches, options:{:force_run => false}.merge(TOOL_COLLECTION_TEST_TASKS)) else - @ceedling[:streaminator].stream_puts( "\nERROR: Found no tests matching pattern /#{args.regex}/.", Verbosity::ERRORS ) + @ceedling[:streaminator].stream_puts( "Found no tests matching pattern /#{args.regex}/", Verbosity::ERRORS ) end end @@ -63,7 +63,7 @@ namespace TEST_SYM do if (matches.size > 0) @ceedling[:test_invoker].setup_and_invoke(tests:matches, options:{:force_run => false}.merge(TOOL_COLLECTION_TEST_TASKS)) else - @ceedling[:streaminator].stream_puts( "\nERROR: Found no tests including the given path or path component.", Verbosity::ERRORS ) + @ceedling[:streaminator].stream_puts( "Found no tests including the given path or path component", Verbosity::ERRORS ) end end diff --git a/lib/ceedling/test_invoker_helper.rb b/lib/ceedling/test_invoker_helper.rb index d90f4f608..a3983fed7 100644 --- a/lib/ceedling/test_invoker_helper.rb +++ b/lib/ceedling/test_invoker_helper.rb @@ -274,7 +274,7 @@ def generate_executable_now(context:, build_path:, executable:, objects:, flags: lib_paths ) rescue ShellExecutionException => ex if ex.shell_result[:output] =~ /symbol/i - notice = "NOTICE: If the linker reports missing symbols, the following may be to blame:\n" + + notice = "If the linker reports missing symbols, the following may be to blame:\n" + " 1. This test lacks #include statements corresponding to needed source files (see note below).\n" + " 2. Project file paths omit source files corresponding to #include statements in this test.\n" + " 3. Complex macros, #ifdefs, etc. have obscured correct #include statements in this test.\n" + @@ -300,7 +300,7 @@ def generate_executable_now(context:, build_path:, executable:, objects:, flags: "See the docs on conventions, paths, preprocessing, compilation symbols, and build directive macros.\n\n" # Print helpful notice - @streaminator.stream_puts(notice, Verbosity::COMPLAIN) + @streaminator.stream_puts( notice, Verbosity::COMPLAIN, LogLabels::NOTICE ) end # Re-raise the exception diff --git a/lib/ceedling/tool_executor.rb b/lib/ceedling/tool_executor.rb index 9b3dd6a8b..86eea5bd0 100644 --- a/lib/ceedling/tool_executor.rb +++ b/lib/ceedling/tool_executor.rb @@ -132,8 +132,8 @@ def expandify_element(tool_name, element, *args) args_index = ($2.to_i - 1) if (args.nil? or args[args_index].nil?) - error = "ERROR: Tool '#{tool_name}' expected valid argument data to accompany replacement operator #{$1}." - raise CeedlingException.new(error) + error = "Tool '#{tool_name}' expected valid argument data to accompany replacement operator #{$1}." + raise CeedlingException.new( error ) end match = /#{Regexp.escape($1)}/ @@ -146,7 +146,6 @@ def expandify_element(tool_name, element, *args) # handle inline ruby execution if (element =~ RUBY_EVAL_REPLACEMENT_PATTERN) - puts("HERE") element.replace(eval($1)) end @@ -178,8 +177,8 @@ def dehashify_argument_elements(tool_name, hash) expand = hash[hash.keys[0]] if (expand.nil?) - error = "ERROR: Tool '#{tool_name}' could not expand nil elements for substitution string '#{substitution}'." - raise CeedlingException.new(error) + error = "Tool '#{tool_name}' could not expand nil elements for substitution string '#{substitution}'." + raise CeedlingException.new( error ) end # array-ify expansion input if only a single string @@ -196,19 +195,19 @@ def dehashify_argument_elements(tool_name, hash) elsif (@system_wrapper.constants_include?(item)) const = Object.const_get(item) if (const.nil?) - error = "ERROR: Tool '#{tool_name}' found constant '#{item}' to be nil." - raise CeedlingException.new(error) + error = "Tool '#{tool_name}' found constant '#{item}' to be nil." + raise CeedlingException.new( error ) else elements << const end elsif (item.class == Array) elements << item elsif (item.class == String) - error = "ERROR: Tool '#{tool_name}' cannot expand nonexistent value '#{item}' for substitution string '#{substitution}'." - raise CeedlingException.new(error) + error = "Tool '#{tool_name}' cannot expand nonexistent value '#{item}' for substitution string '#{substitution}'." + raise CeedlingException.new( error ) else - error = "ERROR: Tool '#{tool_name}' cannot expand value having type '#{item.class}' for substitution string '#{substitution}'." - raise CeedlingException.new(error) + error = "Tool '#{tool_name}' cannot expand value having type '#{item.class}' for substitution string '#{substitution}'." + raise CeedlingException.new( error ) end end diff --git a/lib/ceedling/tool_executor_helper.rb b/lib/ceedling/tool_executor_helper.rb index f491bf3d6..58982890c 100644 --- a/lib/ceedling/tool_executor_helper.rb +++ b/lib/ceedling/tool_executor_helper.rb @@ -106,7 +106,7 @@ def print_happy_results(command_str, shell_result, boom=true) # def print_error_results(command_str, shell_result, boom=true) if ((shell_result[:exit_code] != 0) and boom) - output = "ERROR: Shell command failed.\n" + output = "Shell command failed.\n" output += "> Shell executed command:\n" output += "'#{command_str}'\n" output += "> Produced output:\n" if (not shell_result[:output].empty?) @@ -115,7 +115,7 @@ def print_error_results(command_str, shell_result, boom=true) output += "> And then likely crashed.\n" if (shell_result[:exit_code] == nil) output += "\n" - @streaminator.stream_puts(output, Verbosity::ERRORS) + @streaminator.stream_puts( output, Verbosity::ERRORS ) end end end diff --git a/lib/ceedling/tool_validator.rb b/lib/ceedling/tool_validator.rb index 45150357c..fbffdf128 100644 --- a/lib/ceedling/tool_validator.rb +++ b/lib/ceedling/tool_validator.rb @@ -42,7 +42,7 @@ def validate_executable(tool:, name:, extension:, respect_optional:, boom:) if (executable.nil? or executable.empty?) error = "#{name} is missing :executable in its configuration." if !boom - @streaminator.stream_puts( 'ERROR: ' + error, Verbosity::ERRORS ) + @streaminator.stream_puts( error, Verbosity::ERRORS ) return false end @@ -111,7 +111,7 @@ def validate_executable(tool:, name:, extension:, respect_optional:, boom:) # Otherwise, log error if !exists - @streaminator.stream_puts( 'ERROR: ' + error, Verbosity::ERRORS ) + @streaminator.stream_puts( error, Verbosity::ERRORS ) end return exists @@ -130,8 +130,8 @@ def validate_stderr_redirect(tool:, name:, boom:) raise CeedlingException.new( error ) if boom # Otherwise log error - @streaminator.stream_puts( 'ERROR: ' + error, Verbosity::ERRORS) - return false + @streaminator.stream_puts( error, Verbosity::ERRORS ) + return false end elsif redirect.class != String raise CeedlingException.new( "#{name} ↳ :stderr_redirect is neither a recognized value nor custom string." ) diff --git a/plugins/bullseye/lib/bullseye.rb b/plugins/bullseye/lib/bullseye.rb index 04f991a26..b788f413f 100755 --- a/plugins/bullseye/lib/bullseye.rb +++ b/plugins/bullseye/lib/bullseye.rb @@ -152,7 +152,7 @@ def report_coverage_results_all(coverage) results[:coverage][:branches] = $1.to_i end - @ceedling[:plugin_reportinator].run_report($stdout, @coverage_template_all, results) + @ceedling[:plugin_reportinator].run_report( @coverage_template_all, results ) end def report_per_function_coverage_results(sources) @@ -169,7 +169,7 @@ def report_per_function_coverage_results(sources) coverage_results = shell_results[:output].deep_clone coverage_results.sub!(/.*\n.*\n/,'') # Remove the Bullseye tool banner if (coverage_results =~ /warning cov814: report is empty/) - coverage_results = "WARNING: #{source} contains no coverage data!\n\n" + coverage_results = "#{source} contains no coverage data" @ceedling[:streaminator].stream_puts(coverage_results, Verbosity::COMPLAIN) else coverage_results += "\n" diff --git a/plugins/gcov/gcov.rake b/plugins/gcov/gcov.rake index 1b15551ca..549080484 100755 --- a/plugins/gcov/gcov.rake +++ b/plugins/gcov/gcov.rake @@ -37,16 +37,16 @@ namespace GCOV_SYM do desc 'Run code coverage for all tests' task all: [:prepare] do - @ceedling[:test_invoker].setup_and_invoke(tests:COLLECTION_ALL_TESTS, context:GCOV_SYM, options:TOOL_COLLECTION_GCOV_TASKS) + @ceedling[:test_invoker].setup_and_invoke( tests:COLLECTION_ALL_TESTS, context:GCOV_SYM, options:TOOL_COLLECTION_GCOV_TASKS ) end desc 'Run single test w/ coverage ([*] test or source file name, no path).' task :* do - message = "\nOops! '#{GCOV_ROOT_NAME}:*' isn't a real task. " \ + message = "Oops! '#{GCOV_ROOT_NAME}:*' isn't a real task. " \ "Use a real test or source file name (no path) in place of the wildcard.\n" \ - "Example: rake #{GCOV_ROOT_NAME}:foo.c\n\n" + "Example: `ceedling #{GCOV_ROOT_NAME}:foo.c`" - @ceedling[:streaminator].stream_puts(message) + @ceedling[:streaminator].stream_puts( message, Verbosity::ERRORS ) end desc 'Run tests by matching regular expression pattern.' @@ -58,7 +58,7 @@ namespace GCOV_SYM do end if !matches.empty? - @ceedling[:test_invoker].setup_and_invoke(tests:matches, context:GCOV_SYM, options:{ force_run: false }.merge(TOOL_COLLECTION_GCOV_TASKS)) + @ceedling[:test_invoker].setup_and_invoke( tests:matches, context:GCOV_SYM, options:{ force_run: false }.merge(TOOL_COLLECTION_GCOV_TASKS) ) else @ceedling[:streaminator].stream_puts("\nFound no tests matching pattern /#{args.regex}/.") end @@ -73,9 +73,9 @@ namespace GCOV_SYM do end if !matches.empty? - @ceedling[:test_invoker].setup_and_invoke(tests:matches, context:GCOV_SYM, options:{ force_run: false }.merge(TOOL_COLLECTION_GCOV_TASKS)) + @ceedling[:test_invoker].setup_and_invoke( tests:matches, context:GCOV_SYM, options:{ force_run: false }.merge(TOOL_COLLECTION_GCOV_TASKS) ) else - @ceedling[:streaminator].stream_puts("\nFound no tests including the given path or path component.") + @ceedling[:streaminator].stream_puts( 'Found no tests including the given path or path component', Verbosity::ERRORS ) end end @@ -88,7 +88,7 @@ namespace GCOV_SYM do end ]) do |test| @ceedling[:rake_wrapper][:prepare].invoke - @ceedling[:test_invoker].setup_and_invoke(tests:[test.source], context:GCOV_SYM, options:TOOL_COLLECTION_GCOV_TASKS) + @ceedling[:test_invoker].setup_and_invoke( tests:[test.source], context:GCOV_SYM, options:TOOL_COLLECTION_GCOV_TASKS ) end end diff --git a/plugins/gcov/lib/gcov.rb b/plugins/gcov/lib/gcov.rb index 9e0ec4f12..83030fc4d 100755 --- a/plugins/gcov/lib/gcov.rb +++ b/plugins/gcov/lib/gcov.rb @@ -188,16 +188,17 @@ def console_coverage_summaries() # Handle errors instead of raising a shell exception if shell_results[:exit_code] != 0 - debug = "ERROR: gcov error (#{shell_results[:exit_code]}) while processing #{filename}... #{results}" - @ceedling[:streaminator].stream_puts(debug, Verbosity::DEBUG) - @ceedling[:streaminator].stream_puts("WARNING: gcov was unable to process coverage for #{filename}\n", Verbosity::COMPLAIN) + debug = "gcov error (#{shell_results[:exit_code]}) while processing #{filename}... #{results}" + @ceedling[:streaminator].stream_puts( debug, Verbosity::DEBUG, LogLabels::ERROR ) + @ceedling[:streaminator].stream_puts( "gcov was unable to process coverage for #{filename}", Verbosity::COMPLAIN ) next # Skip to next loop iteration end # A source component may have been compiled with coverage but none of its code actually called in a test. # In this case, versions of gcov may not produce an error, only blank results. if results.empty? - @ceedling[:streaminator].stream_puts("NOTICE: No functions called or code paths exercised by test for #{filename}\n", Verbosity::COMPLAIN) + msg = "No functions called or code paths exercised by test for #{filename}" + @ceedling[:streaminator].stream_puts( msg, Verbosity::COMPLAIN, LogLabels:NOTICE ) next # Skip to next loop iteration end @@ -207,8 +208,8 @@ def console_coverage_summaries() # Extract (relative) filepath from results and expand to absolute path matches = results.match(/File\s+'(.+)'/) if matches.nil? or matches.length() != 2 - msg = "ERROR: Could not extract filepath via regex from gcov results for #{test}::#{File.basename(source)}" - @ceedling[:streaminator].stream_puts( msg, Verbosity::DEBUG ) + msg = "Could not extract filepath via regex from gcov results for #{test}::#{File.basename(source)}" + @ceedling[:streaminator].stream_puts( msg, Verbosity::DEBUG, LogLabels:ERROR ) else # Expand to full path from likely partial path to ensure correct matches on source component within gcov results _source = File.expand_path(matches[1]) @@ -223,7 +224,7 @@ def console_coverage_summaries() # Otherwise, found no coverage results else - msg = "WARNING: Found no coverage results for #{test}::#{File.basename(source)}\n" + msg = "Found no coverage results for #{test}::#{File.basename(source)}" @ceedling[:streaminator].stream_puts( msg, Verbosity::COMPLAIN ) end end diff --git a/plugins/gcov/lib/gcovr_reportinator.rb b/plugins/gcov/lib/gcovr_reportinator.rb index 70f0b20dd..93c5c7aec 100644 --- a/plugins/gcov/lib/gcovr_reportinator.rb +++ b/plugins/gcov/lib/gcovr_reportinator.rb @@ -348,7 +348,7 @@ def gcovr_exec_exception?(opts, exitcode, boom) if boom raise CeedlingException.new(msg) else - @streaminator.stream_puts('WARNING: ' + msg, Verbosity::COMPLAIN) + @streaminator.stream_puts( msg, Verbosity::COMPLAIN ) # Clear bit in exit code exitcode &= ~2 end @@ -360,7 +360,7 @@ def gcovr_exec_exception?(opts, exitcode, boom) if boom raise CeedlingException.new(msg) else - @streaminator.stream_puts('WARNING: ' + msg, Verbosity::COMPLAIN) + @streaminator.stream_puts( msg, Verbosity::COMPLAIN ) # Clear bit in exit code exitcode &= ~4 end @@ -372,7 +372,7 @@ def gcovr_exec_exception?(opts, exitcode, boom) if boom raise CeedlingException.new(msg) else - @streaminator.stream_puts('WARNING: ' + msg, Verbosity::COMPLAIN) + @streaminator.stream_puts( msg, Verbosity::COMPLAIN ) # Clear bit in exit code exitcode &= ~8 end @@ -384,7 +384,7 @@ def gcovr_exec_exception?(opts, exitcode, boom) if boom raise CeedlingException.new(msg) else - @streaminator.stream_puts('WARNING: ' + msg, Verbosity::COMPLAIN) + @streaminator.stream_puts( msg, Verbosity::COMPLAIN ) # Clear bit in exit code exitcode &= ~16 end diff --git a/plugins/gcov/lib/reportgenerator_reportinator.rb b/plugins/gcov/lib/reportgenerator_reportinator.rb index cf1b54fc3..6e1349c04 100644 --- a/plugins/gcov/lib/reportgenerator_reportinator.rb +++ b/plugins/gcov/lib/reportgenerator_reportinator.rb @@ -104,7 +104,7 @@ def generate_reports(opts) end end else - @streaminator.stream_puts("\nWARNING: No matching .gcno coverage files found.", Verbosity::COMPLAIN) + @streaminator.stream_puts( "No matching .gcno coverage files found", Verbosity::COMPLAIN ) end end