Skip to content

Commit

Permalink
Merge pull request #942 from ThrowTheSwitch/test/refactor_loginator_a…
Browse files Browse the repository at this point in the history
…nd_batchinator

Test/refactor loginator and batchinator
  • Loading branch information
mvandervoord authored Oct 23, 2024
2 parents 9fc30fb + 37fc98a commit 8ce41a0
Show file tree
Hide file tree
Showing 15 changed files with 161 additions and 134 deletions.
15 changes: 2 additions & 13 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby: ['3.0', '3.1', '3.2']
ruby: ['3.0', '3.1', '3.2', '3.3']
steps:
# Use a cache for our tools to speed up testing
- uses: actions/cache@v4
Expand Down Expand Up @@ -131,7 +131,7 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby: ['3.0', '3.1', '3.2']
ruby: ['3.0', '3.1', '3.2', '3.3']
steps:
# Use a cache for our tools to speed up testing
- uses: actions/cache@v4
Expand Down Expand Up @@ -288,14 +288,3 @@ jobs:
asset_name: ceedling-${{ env.ceedling_build }}.gem
asset_content_type: test/x-gemfile

# - name: Upload Pre-Release Gem
# uses: softprops/action-gh-release@v2
# with:
# # repo_token: "${{ secrets.GITHUB_TOKEN }}"
# body: |
# [Release Notes](${{ github.workspace }}/docs/ReleaseNotes.md)
# name: ${{ env.full_ver }}
# prerelease: true
# files: |
# *.gem

2 changes: 2 additions & 0 deletions assets/project_as_gem.yml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@
# You will need to have gcov and gcovr both installed to make it work.
# For more information on these options, see docs in plugins/gcov
:gcov:
:summaries: TRUE # Enable simple coverage summaries to console after tests
:report_task: FALSE # Disabled dedicated report generation task (this enables automatic report generation)
:utilities:
- gcovr # Use gcovr to create the specified reports (default).
#- ReportGenerator # Use ReportGenerator to create the specified reports.
Expand Down
2 changes: 2 additions & 0 deletions assets/project_with_guts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@
# You will need to have gcov and gcovr both installed to make it work.
# For more information on these options, see docs in plugins/gcov
:gcov:
:summaries: TRUE # Enable simple coverage summaries to console after tests
:report_task: FALSE # Disabled dedicated report generation task (this enables automatic report generation)
:utilities:
- gcovr # Use gcovr to create the specified reports (default).
#- ReportGenerator # Use ReportGenerator to create the specified reports.
Expand Down
59 changes: 34 additions & 25 deletions lib/ceedling/build_batchinator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,39 +52,48 @@ def exec(workload:, things:, &block)

threads = (1..workers).collect do
thread = Thread.new do
begin
# Run tasks until there are no more enqueued
loop do
# pop(true) is non-blocking and raises ThreadError when queue is empty
yield @queue.pop(true)
end

# First, handle thread exceptions (should always be due to empty queue)
rescue ThreadError => e
# Typical case: do nothing and allow thread to wind down
Thread.handle_interrupt(Exception => :never) do
begin
Thread.handle_interrupt(Exception => :immediate) do
# Run tasks until there are no more enqueued
loop do
# pop(true) is non-blocking and raises ThreadError when queue is empty
yield @queue.pop(true)
end
end

# First, handle thread exceptions (should always be due to empty queue)
rescue ThreadError => e
# Typical case: do nothing and allow thread to wind down

# ThreadError outside scope of expected empty queue condition
unless e.message.strip.casecmp("queue empty")
@loginator.log(e.message, Verbosity::ERRORS, LogLabels::EXCEPTION )

# Shutdown all worker threads
shutdown_threads(threads)

raise(e) # Raise exception again
end

# Second, catch every other kind of exception so we can intervene with thread cleanup.
# Generally speaking, catching Exception is a no-no, but we must in this case.
# Raise the exception again so that:
# 1. Calling code knows something bad happened and handles appropriately
# 2. Ruby runtime can handle most serious problems
rescue Exception => e
@loginator.log(e.message, Verbosity::ERRORS, LogLabels::EXCEPTION )

# ThreadError outside scope of expected empty queue condition
unless e.message.strip.casecmp("queue empty")
# Shutdown all worker threads
shutdown_threads(threads)
shutdown_threads(threads)

raise(e) # Raise exception again
raise(e) # Raise exception again after intervening
end

# Second, catch every other kind of exception so we can intervene with thread cleanup.
# Generally speaking, catching Exception is a no-no, but we must in this case.
# Raise the exception again so that:
# 1. Calling code knows something bad happened and handles appropriately
# 2. Ruby runtime can handle most serious problems
rescue Exception => e
# Shutdown all worker threads
shutdown_threads(threads)

raise(e) # Raise exception again after intervening
end
end

# Hand thread to Enumerable collect() routine
thread.abort_on_exception = true
thread
end

Expand Down
21 changes: 11 additions & 10 deletions lib/ceedling/file_path_collection_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ def setup()
@working_dir_path = Pathname.new( Dir.pwd() )
end


# Build up a directory path list from one or more strings or arrays of (+:/-:) simple paths & globs
def collect_paths(paths)
plus = Set.new # All real, expanded directory paths to add
Expand Down Expand Up @@ -78,11 +77,7 @@ def collect_paths(paths)

# Use Set subtraction operator to remove any excluded paths
paths = (plus - minus).to_a

paths.map! do |path|
# Reform path from full absolute to nice, neat relative path instead
(Pathname.new( path ).relative_path_from( @working_dir_path )).to_s()
end
paths.map! {|path| shortest_path_from_working(path) }

return paths
end
Expand Down Expand Up @@ -124,13 +119,19 @@ def revise_filelist(list, revisions)

# Use Set subtraction operator to remove any excluded paths
paths = (plus - minus).to_a
paths.map! {|path| shortest_path_from_working(path) }

paths.map! do |path|
return FileList.new( paths )
end

def shortest_path_from_working(path)
begin
# Reform path from full absolute to nice, neat relative path instead
(Pathname.new( path ).relative_path_from( @working_dir_path )).to_s()
(Pathname.new( path ).relative_path_from( @working_dir_path )).to_s
rescue
# If we can't form a relative path between these paths, use the absolute
path
end

return FileList.new( paths )
end

end
5 changes: 3 additions & 2 deletions lib/ceedling/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,13 @@ def generate_object_file_c(
:flags => flags,
:defines => defines,
:list => list,
:dependencies => dependencies
:dependencies => dependencies,
:msg => String(msg)
}

@plugin_manager.pre_compile_execute(arg_hash)

msg = String(msg)
msg = arg_hash[:msg]
msg = @reportinator.generate_module_progress(
operation: "Compiling",
module_name: module_name,
Expand Down
94 changes: 73 additions & 21 deletions lib/ceedling/loginator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Loginator
constructor :verbosinator, :file_wrapper, :system_wrapper

def setup()
$loginator = self
@decorators = false

# Friendly robustification for certain testing scenarios
Expand All @@ -43,8 +44,68 @@ def setup()

@project_logging = false
@log_filepath = nil

@queue = Queue.new
@worker = Thread.new do
# Run tasks until there are no more enqueued
@done = false
while !@done do
Thread.handle_interrupt(Exception => :never) do
begin
Thread.handle_interrupt(Exception => :immediate) do
# pop(false) is blocking and should just hang here and wait for next message
item = @queue.pop(false)
if (item.nil?)
@done = true
next
end

# pick out the details
message = item[:message]
label = item[:label]
verbosity = item[:verbosity]
stream = item[:stream]

# Write to log as though Verbosity::DEBUG (no filtering at all) but without fun characters
if @project_logging
file_msg = message.dup() # Copy for safe inline modifications

# Add labels
file_msg = format( file_msg, verbosity, label, false )

# Note: In practice, file-based logging only works with trailing newlines (i.e. `log()` calls)
# `out()` calls will be a little ugly in the log file, but these are typically only
# used for console logging anyhow.
logfile( sanitize( file_msg, false ), extract_stream_name( stream ) )
end

# Only output to console when message reaches current verbosity level
if !stream.nil? && (@verbosinator.should_output?( verbosity ))
# Add labels and fun characters
console_msg = format( message, verbosity, label, @decorators )

# Write to output stream after optionally removing any problematic characters
stream.print( sanitize( console_msg, @decorators ) )
end
end
rescue ThreadError
@done = true
rescue Exception => e
puts e.inspect
end
end
end
end
end

def wrapup
begin
@queue.close
@worker.join
rescue
#If we failed at this point, just give up on it
end
end

def set_logfile( log_filepath )
if !log_filepath.empty?
Expand Down Expand Up @@ -89,27 +150,14 @@ def log(message="\n", verbosity=Verbosity::NORMAL, label=LogLabels::AUTO, stream
# Message contatenated with "\n" (unless it aready ends with a newline)
message += "\n" unless message.end_with?( "\n" )

# Write to log as though Verbosity::DEBUG (no filtering at all) but without fun characters
if @project_logging
file_msg = message.dup() # Copy for safe inline modifications

# Add labels
file_msg = format( file_msg, verbosity, label, false )

# Note: In practice, file-based logging only works with trailing newlines (i.e. `log()` calls)
# `out()` calls will be a little ugly in the log file, but these are typically only
# used for console logging anyhow.
logfile( sanitize( file_msg, false ), extract_stream_name( stream ) )
end

# Only output to console when message reaches current verbosity level
return if !(@verbosinator.should_output?( verbosity ))

# Add labels and fun characters
console_msg = format( message, verbosity, label, @decorators )

# Write to output stream after optionally removing any problematic characters
stream.print( sanitize( console_msg, @decorators ) )
# Add item to the queue
item = {
:message => message,
:verbosity => verbosity,
:label => label,
:stream => stream
}
@queue << item
end


Expand Down Expand Up @@ -262,3 +310,7 @@ def logfile(string, stream='')
end

end

END {
$loginator.wrapup unless $loginator.nil?
}
1 change: 1 addition & 0 deletions lib/ceedling/objects.yml
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ test_invoker:
- generator
- test_context_extractor
- file_path_utils
- file_finder
- file_wrapper
- verbosinator

Expand Down
4 changes: 2 additions & 2 deletions lib/ceedling/plugin_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ def load_programmatic_plugins(plugins, system_objects)

# Add plugins to hash of all system objects
system_objects[hash[:plugin].downcase().to_sym()] = object
rescue
@loginator.log( "Exception raised while trying to load plugin: #{hash[:plugin]}", Verbosity::ERRORS )
rescue
@loginator.log( "Exception raised while trying to load plugin: #{hash[:plugin]}", Verbosity::ERRORS, LogLabels::EXCEPTION )
raise # Raise again for backtrace, etc.
end
end
Expand Down
3 changes: 3 additions & 0 deletions lib/ceedling/rakefile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,11 @@ def test_failures_handler()
ops_done = SystemWrapper.time_stopwatch_s()
log_runtime( 'operations', start_time, ops_done, CEEDLING_APPCFG.build_tasks? )
boom_handler( @ceedling[:loginator], ex )
@ceedling[:loginator].wrapup
exit(1)
end

@ceedling[:loginator].wrapup
exit(0)
else
msg = "Ceedling could not complete operations because of errors"
Expand All @@ -136,6 +138,7 @@ def test_failures_handler()
rescue => ex
boom_handler( @ceedling[:loginator], ex)
ensure
@ceedling[:loginator].wrapup
exit(1)
end
end
Expand Down
Loading

0 comments on commit 8ce41a0

Please sign in to comment.