Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test/refactor loginator and batchinator #942

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading