Skip to content
Open
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
142 changes: 142 additions & 0 deletions .github/scripts/mcdc-analyze.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/bin/bash

# Redirect all echo outputs to mcdc_results.txt and capture gcov output
exec > >(tee -a mcdc_results.txt) 2>&1

# Pass the test modules after running unit tests
# Ex. echo "MODULES=$(grep -oP 'Test #\d+: \K[\w\-\_]+' test_results.txt | tr '\n' ' ' | sed 's/ $//')" >> $GITHUB_ENV
if [ -n "$MODULES" ]; then
modules="$MODULES"
echo "Test modules provided: "
for module in $modules; do
echo "$module"
done
else
echo "No test modules provided."
exit 1
fi

# Initialize overall counters
overall_total_functions=0
overall_total_covered_functions=0
overall_file_count=0
overall_no_conditions_count=0
module_count=0


# Show coverage for each file in a module and summary coverage for each module
for module in $modules; do
module_name=$(basename "$module")

# Skip specific files and directories
if [[ "$module_name" == "core-cpu1" || \
"$module_name" == "Makefile" || \
"$module_name" == "CTestTestfile" || \
"$module_name" == "cmake_install" || \
"$module_name" == "gmon" || \
"$module_name" == *"stubs"* ]]; then
continue
fi

module_name_no_testrunner=$(echo "$module_name" | sed 's/-testrunner$//')

echo " "
echo "Processing $module_name_no_testrunner module..."

# Initialize module-level counters
total_functions=0
total_covered_functions=0
file_count=0
no_conditions_count=0

module_dirs=""

if [ -n "$BASE_DIR" ]; then
# If BASE_DIR is provided, search within the BASE_DIR for the module directories.
# FIX, module dirs doesn't always show
module_dirs=$(find "$BASE_DIR" -type d -name "*${module_name}*")
echo "Base directory specified: $BASE_DIR"
echo "Searching for .gcda directory..."
else
# Otherwise, look for the default module directories.
module_dirs=$(find "build/native/default_cpu1" -type d -name "*${module_name}*.dir")
echo "No base directory provided: Searching for .gcda directory..."
fi

if [ -n "$module_dirs" ]; then
for module_dir in $module_dirs; do
echo "Found module directory: $module_dir"

parent_dir=$(dirname "$module_dir")
echo "Searching for .gcda files under parent directory: $parent_dir..."
gcda_files=$(find "$parent_dir" -type d -name "*${module_name_no_testrunner}*.dir" -exec find {} -type f -name "*.gcda" \;)

if [ -n "$gcda_files" ]; then
for gcda_file in $gcda_files; do
c_file=$(echo "$gcda_file" | sed 's/\.gcda$/.c/')

echo "Processing corresponding .c file: $c_file"
echo "Running gcov on $c_file..."

# Capture gcov output and remove header files
gcov_output=$(gcov -abcgi "$c_file" | sed "/\.h/,/^$/d")

# Output the gcov result of each file and save to mcdc_results.txt
echo "$gcov_output" | tee -a mcdc_results.txt

# Process gcov results for coverage summary
while IFS= read -r line; do
if [[ $line == *"Condition outcomes covered:"* ]]; then
condition_covered=$(echo "$line" | grep -oP 'Condition outcomes covered:\K[0-9.]+')
total_conditions_in_file=$(echo "$line" | grep -oP 'of \K[0-9]+')

covered_functions_in_file=$(awk -v pct="$condition_covered" -v total="$total_conditions_in_file" 'BEGIN {printf "%.2f", (pct / 100) * total}')

total_functions=$((total_functions + total_conditions_in_file))
total_covered_functions=$(awk -v covered="$total_covered_functions" -v new_covered="$covered_functions_in_file" 'BEGIN {printf "%.2f", covered + new_covered}')

file_count=$((file_count + 1))
elif [[ $line == *"No conditions"* ]]; then
no_conditions_count=$((no_conditions_count + 1))
fi
done <<< "$gcov_output"
done
else
echo "No .gcda files found for $module_name under parent directory $parent_dir."
fi
done
else
echo "Directory for module $module_name \(e.g., ${module_name}.dir\) not found."
fi

if [ "$total_functions" -ne 0 ]; then
average_condition_coverage=$(awk -v covered="$total_covered_functions" -v total="$total_functions" 'BEGIN {printf "%.2f", (covered / total) * 100}')
else
average_condition_coverage=0
fi

overall_total_functions=$((overall_total_functions + total_functions))
overall_total_covered_functions=$(awk -v covered="$overall_total_covered_functions" -v new_covered="$total_covered_functions" 'BEGIN {printf "%.2f", covered + new_covered}')
overall_file_count=$((overall_file_count + file_count))
overall_no_conditions_count=$((overall_no_conditions_count + no_conditions_count))

module_count=$((module_count + 1))

echo "Summary for $module_name_no_testrunner module:"
echo " Total files processed: $file_count"
echo " Number of files with no condition data: $no_conditions_count"
echo " Condition outcomes covered: ${average_condition_coverage}% of $total_functions"
echo " "
done

if [ "$overall_total_functions" -ne 0 ]; then
overall_condition_coverage=$(awk -v covered="$overall_total_covered_functions" -v total="$overall_total_functions" 'BEGIN {printf "%.2f", (covered / total) * 100}')
else
overall_condition_coverage=0
fi

echo " "
echo "Overall summary:"
echo " Total files processed: $overall_file_count"
echo " Number of files with no condition data: $overall_no_conditions_count"
echo " Overall condition outcomes covered: ${overall_condition_coverage}% of $overall_total_functions"
203 changes: 203 additions & 0 deletions .github/scripts/mcdc-compare.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
#!/bin/bash

exec > >(tee -a mcdc_compare.txt) 2>&1

# Function to check if a file exists and return an error message for missing files
check_file_exists() {
file=$1
if [ ! -f "$file" ]; then
echo "Error: File '$file' does not exist."
missing_files=true
fi
}

# Function to extract the relevant numbers from a module's "Summary for module" section
extract_module_numbers() {
file=$1
module=$2

total_files_processed=$(sed -n "/^Summary for ${module} module:/,/^$/p" "$file" | head -n 4 | grep -Po 'Total files processed:\s*\K\d*')
no_condition_data=$(sed -n "/^Summary for ${module} module:/,/^$/p" "$file" | head -n 4 | grep -Po 'Number of files with no condition data:\s*\K\d+')
condition_outcomes_covered_percent=$(sed -n "/^Summary for ${module} module:/,/^$/p" "$file" | head -n 4 | grep -Po 'Condition outcomes covered:\s*\K[0-9]+(\.[0-9]+)?')
condition_outcomes_out_of=$(sed -n "/^Summary for ${module} module:/,/^$/p" "$file" | head -n 4 | grep -Po 'Condition outcomes covered:.*of\s*\K\d*')

echo "$total_files_processed $no_condition_data $condition_outcomes_covered_percent $condition_outcomes_out_of"
}

# Compare results for each module between two files
compare_mcdc_results() {
main_results_file=$1
pr_results_file=$2
modules_file=$3

# Initialize a flag to track if any files are missing
missing_files=false

# Check if the files exist before proceeding
check_file_exists "$main_results_file"
check_file_exists "$pr_results_file"
check_file_exists "$modules_file"

# If any files are missing, exit early
if [ "$missing_files" = true ]; then
echo "Error: One or more input files are missing. Exiting."
exit 1
fi

# Read modules from modules.txt (passed as argument)
modules=$(cat "$modules_file")

# Check if modules are empty or not
if [ -z "$modules" ]; then
echo "Error: No modules found in $modules_file"
exit 1
fi

# Initialize variables to store the output for modules with and without changes
modules_with_changes=""
modules_without_changes=""

# Loop through all modules to compare each one
for module in $modules; do

# Extract numbers for the main results file and PR results file for the current module
read main_total_files main_no_condition main_condition_covered_percent main_condition_out_of <<< $(extract_module_numbers "$main_results_file" "$module")
read pr_total_files pr_no_condition pr_condition_covered_percent pr_condition_out_of <<< $(extract_module_numbers "$pr_results_file" "$module")

# Echo numbers extracted from each file for each module
echo -e "\nResults for module: $module"
echo "PR Branch - Total files processed: $pr_total_files, No condition data: $pr_no_condition, Covered condition %: $pr_condition_covered_percent%, Out of value: $pr_condition_out_of"
echo "Main Branch - Total files processed: $main_total_files, No condition data: $main_no_condition, Covered condition %: $main_condition_covered_percent%, Out of value: $main_condition_out_of"

# Initialize variables to store differences
total_files_diff=""
no_condition_data_diff=""
condition_outcomes_covered_diff_percent=""
condition_outcomes_out_of_diff=""

# Calculate difference between files
total_files_diff=$((pr_total_files - main_total_files))
no_condition_data_diff=$((pr_no_condition - main_no_condition))
condition_outcomes_covered_diff_percent=$(echo "$pr_condition_covered_percent - $main_condition_covered_percent" | bc)
condition_outcomes_out_of_diff=$((pr_condition_out_of - main_condition_out_of))

echo "Differences:"
echo " Total files processed difference: $total_files_diff"
echo " No condition data difference: $no_condition_data_diff"
echo " Covered condition % difference: $condition_outcomes_covered_diff_percent"
echo " Out of value difference: $condition_outcomes_out_of_diff"
echo " "

changes=""

if [ "$total_files_diff" -gt 0 ]; then
changes="${changes} Number of files processed: +$total_files_diff\n"
elif [ "$total_files_diff" -lt 0 ]; then
changes="${changes} Number of files processed: $total_files_diff\n"
fi

if [ "$no_condition_data_diff" -gt 0 ]; then
changes="${changes} Number of files with no condition data: +$no_condition_data_diff\n"
elif [ "$no_condition_data_diff" -lt 0 ]; then
changes="${changes} Number of files with no condition data: $no_condition_data_diff\n"
fi

if [ $(echo "$condition_outcomes_covered_diff_percent > 0" | bc) -eq 1 ]; then
changes="${changes} Percentage of covered conditions: +$condition_outcomes_covered_diff_percent%\n"
elif [ $(echo "$condition_outcomes_covered_diff_percent < 0" | bc) -eq 1 ]; then
changes="${changes} Percentage of covered conditions: $condition_outcomes_covered_diff_percent%\n"
fi

if [ "$condition_outcomes_out_of_diff" -gt 0 ]; then
changes="${changes} Number of conditions: +$condition_outcomes_out_of_diff\n"
elif [ "$condition_outcomes_out_of_diff" -lt 0 ]; then
changes="${changes} Number of conditions: $condition_outcomes_out_of_diff\n"
fi

if [ -n "$changes" ]; then
modules_with_changes="${modules_with_changes} $module\n$changes\n"
else
modules_without_changes="${modules_without_changes} $module\n"
fi
done

echo " "
echo "MC/DC results compared to latest dev branch:"
echo " "
echo "Modules with changes:"
echo -e "$modules_with_changes"
echo "Modules without changes:"
echo -e "$modules_without_changes"

# Write results to mcdc_comment.txt / pull request
if [ -n "$modules_with_changes" ]; then
echo "MC/DC results compared to latest dev branch:" > mcdc_comment.txt
echo "" >> mcdc_comment.txt
echo "Modules with changes:" >> mcdc_comment.txt
echo -e "$modules_with_changes" >> mcdc_comment.txt
echo "" >> mcdc_comment.txt
echo "See file uncovered.json for more details"
else
echo "No MC/DC changes were made." > mcdc_comment.txt
fi

}

# creates single json file that contains info on all uncovered branches
generate_json_report() {
jq_script=$(find $GITHUB_WORKSPACE -name "uncovered_filter.jq" | tail -n 1)
if [ -z "$jq_script" ]; then
echo "Error: Could not find uncovered_filter.jq"
return 1
fi

for zipped_file in *.gcov.json.gz; do
if [ -f "$zipped_file" ]; then
base_name="${zipped_file%.gcov.json.gz}"
gunzip -c "$zipped_file" > "${base_name}.json"
if [ -f "${base_name}.json" ]; then
jq -f "$jq_script" "${base_name}.json" > "${base_name}_filtered.json"
else
echo "Error: Failed to decompress $zipped_file"
return 1
fi
else
echo "Warning: No .gcov.json.gz files found"
return 0
fi
done

if ls *_filtered.json 1> /dev/null 2>&1; then
jq -s '.' *_filtered.json > uncovered.json
echo "Successfully created uncovered.json"
else
echo "No filtered JSON files found to merge"
return 1
fi

if jq 'flatten' uncovered.json > temp.json; then
mv temp.json uncovered.json
else
rm -f temp.json
echo "Error processing JSON file"
exit 1
fi

if jq '.' uncovered.json > temp.json; then
mv temp.json uncovered.json
else
rm -f temp.json
echo "Error processing JSON file"
exit 1
fi
}

# Check the script arguments
if [ $# -ne 3 ]; then
echo "Usage: $0 <main_results_file> <pr_results_file> <modules_file>"
exit 1
fi

# Run the comparison function with the provided arguments
generate_json_report
compare_mcdc_results "$1" "$2" "$3"
Loading
Loading