* feat: add support chat translations * feat: add hook to get support group * feat: add new screen to start support chat * feat: show chat with support option in user search screen * style: add empty line to .fvmrc * chore: change min coverage locally to be 99% * chore: update lockfiles * docs: update changelog
252 lines
5.1 KiB
Bash
Executable File
252 lines
5.1 KiB
Bash
Executable File
#!/bin/bash
|
|
# Check Flutter test coverage
|
|
#
|
|
# Usage:
|
|
# ./scripts/check-coverage.sh # Just print coverage %
|
|
# ./scripts/check-coverage.sh --min 99 # Fail if below 99%
|
|
#
|
|
# Excludes auto-generated files:
|
|
# - lib/src/rust/ (flutter_rust_bridge)
|
|
# - lib/l10n/generated/ (flutter_localizations)
|
|
# - *.freezed.dart (freezed)
|
|
# - *.g.dart (json_serializable, etc.)
|
|
|
|
set -euo pipefail
|
|
|
|
# Default values
|
|
LCOV_FILE="coverage/lcov.info"
|
|
MIN_COVERAGE=""
|
|
WARNINGS_FILE=""
|
|
|
|
print_error() {
|
|
printf '%b%s%b\n' "\e[31;1m" "$1" "\e[0m" >&2
|
|
}
|
|
|
|
print_success() {
|
|
printf '%b%s%b\n' "\e[32;1m" "$1" "\e[0m" >&2
|
|
}
|
|
|
|
print_warning() {
|
|
printf '%b%s%b\n' "\e[33;1m" "$1" "\e[0m" >&2
|
|
}
|
|
|
|
raise_error() {
|
|
print_error "$1"
|
|
exit 1
|
|
}
|
|
|
|
parse_arguments() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--min)
|
|
MIN_COVERAGE="$2"
|
|
shift 2
|
|
;;
|
|
--warnings-file)
|
|
WARNINGS_FILE="$2"
|
|
shift 2
|
|
;;
|
|
*)
|
|
LCOV_FILE="$1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
check_lcov_file_presence() {
|
|
if [ ! -f "$LCOV_FILE" ]; then
|
|
raise_error "Error: $LCOV_FILE not found. Run 'flutter test --coverage' first."
|
|
fi
|
|
}
|
|
|
|
is_generated_file() {
|
|
local file="$1"
|
|
if [[ "$file" == *".freezed.dart" ]] ||
|
|
[[ "$file" == *".g.dart" ]] ||
|
|
[[ "$file" == lib/src/rust/* ]] ||
|
|
[[ "$file" == lib/l10n/generated/* ]]; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
is_coverage_ignored() {
|
|
local file="$1"
|
|
grep -q '// coverage:ignore-file' "$file"
|
|
}
|
|
|
|
count_lines() {
|
|
local file="$1"
|
|
wc -l <"$file" | tr -d ' '
|
|
}
|
|
|
|
get_covered_files() {
|
|
grep "^SF:" "$LCOV_FILE" | sed 's/^SF://' | sort -u
|
|
}
|
|
|
|
generate_zero_coverage_record() {
|
|
local file="$1"
|
|
local line_count="$2"
|
|
|
|
echo "SF:$file"
|
|
for ((i = 1; i <= line_count; i++)); do
|
|
echo "DA:$i,0"
|
|
done
|
|
echo "LF:$line_count"
|
|
echo "LH:0"
|
|
echo "end_of_record"
|
|
}
|
|
|
|
print_missing_files_warning() {
|
|
local missing_count="$1"
|
|
shift
|
|
local missing_files=("$@")
|
|
|
|
if [ "$missing_count" -gt 0 ]; then
|
|
print_warning "⚠️ Found $missing_count file(s) with no test coverage:"
|
|
for file in "${missing_files[@]}"; do
|
|
print_warning " - $file"
|
|
done
|
|
fi
|
|
}
|
|
|
|
inject_missing_files() {
|
|
local missing_count=0
|
|
local missing_files=()
|
|
local covered_files
|
|
covered_files=$(get_covered_files)
|
|
|
|
while IFS= read -r -d '' dart_file; do
|
|
dart_file="${dart_file#./}"
|
|
|
|
if is_generated_file "$dart_file"; then
|
|
continue
|
|
fi
|
|
|
|
if is_coverage_ignored "$dart_file"; then
|
|
continue
|
|
fi
|
|
|
|
if echo "$covered_files" | grep -qx "$dart_file"; then
|
|
continue
|
|
fi
|
|
|
|
local line_count
|
|
line_count=$(count_lines "$dart_file")
|
|
|
|
if [ "$line_count" -gt 0 ]; then
|
|
generate_zero_coverage_record "$dart_file" "$line_count" >>"$LCOV_FILE"
|
|
missing_files+=("$dart_file")
|
|
missing_count=$((missing_count + 1))
|
|
fi
|
|
done < <(find lib -name "*.dart" -type f -print0 2>/dev/null)
|
|
|
|
if [ "$missing_count" -gt 0 ]; then
|
|
print_missing_files_warning "$missing_count" "${missing_files[@]}"
|
|
if [ -n "$WARNINGS_FILE" ]; then
|
|
printf '%s\n' "${missing_files[@]}" >"$WARNINGS_FILE"
|
|
fi
|
|
elif [ -n "$WARNINGS_FILE" ]; then
|
|
: >"$WARNINGS_FILE"
|
|
fi
|
|
}
|
|
|
|
filter_generated_files() {
|
|
awk '
|
|
BEGIN { skip = 0 }
|
|
/^SF:/ {
|
|
file = substr($0, 4)
|
|
skip = 0
|
|
if (match(file, /lib\/src\/rust\//) ||
|
|
match(file, /lib\/l10n\/generated\//) ||
|
|
match(file, /\.freezed\.dart$/) ||
|
|
match(file, /\.g\.dart$/)) {
|
|
skip = 1
|
|
}
|
|
}
|
|
/^end_of_record$/ {
|
|
if (!skip) print
|
|
skip = 0
|
|
next
|
|
}
|
|
{ if (!skip) print }
|
|
' "$LCOV_FILE" >"${LCOV_FILE}.tmp"
|
|
mv "${LCOV_FILE}.tmp" "$LCOV_FILE"
|
|
}
|
|
|
|
calculate_coverage() {
|
|
awk '
|
|
BEGIN {
|
|
total_lines = 0
|
|
covered_lines = 0
|
|
skip = 0
|
|
}
|
|
/^SF:/ {
|
|
file = substr($0, 4)
|
|
skip = 0
|
|
if (match(file, /lib\/src\/rust\//) ||
|
|
match(file, /lib\/l10n\/generated\//) ||
|
|
match(file, /\.freezed\.dart$/) ||
|
|
match(file, /\.g\.dart$/)) {
|
|
skip = 1
|
|
}
|
|
}
|
|
/^LF:/ {
|
|
if (!skip) total_lines += substr($0, 4)
|
|
}
|
|
/^LH:/ {
|
|
if (!skip) covered_lines += substr($0, 4)
|
|
}
|
|
END {
|
|
if (total_lines > 0) {
|
|
printf "%.2f\n", (covered_lines / total_lines) * 100
|
|
} else {
|
|
print "0.00"
|
|
}
|
|
}
|
|
' "$LCOV_FILE"
|
|
}
|
|
|
|
check_coverage_threshold() {
|
|
local coverage="$1"
|
|
local min_coverage="$2"
|
|
|
|
if (($(awk "BEGIN {print ($coverage >= $min_coverage)}"))); then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
print_coverage_result() {
|
|
local coverage="$1"
|
|
local min_coverage="$2"
|
|
|
|
if check_coverage_threshold "$coverage" "$min_coverage"; then
|
|
print_success "✅ Coverage: ${coverage}%"
|
|
exit 0
|
|
else
|
|
print_error "❌ Coverage: ${coverage}% (below minimum ${min_coverage}%)"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
parse_arguments "$@"
|
|
check_lcov_file_presence
|
|
inject_missing_files
|
|
filter_generated_files
|
|
|
|
local coverage
|
|
coverage=$(calculate_coverage)
|
|
|
|
if [ -z "$MIN_COVERAGE" ]; then
|
|
echo "$coverage"
|
|
exit 0
|
|
fi
|
|
|
|
print_coverage_result "$coverage" "$MIN_COVERAGE"
|
|
}
|
|
|
|
main "$@"
|