1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 12:41:46 +00:00

feat: overhaul quality checks and expand test suite for clean and optimize features

This commit is contained in:
Tw93
2025-12-31 18:13:37 +08:00
parent 1e8ff30fa1
commit 592f02e6e2
45 changed files with 1506 additions and 910 deletions

View File

@@ -1,126 +1,189 @@
#!/bin/bash
# Unified check script for Mole project
# Runs all quality checks in one command
# Code quality checks for Mole.
# Auto-formats code, then runs lint and syntax checks.
set -e
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
MODE="all"
usage() {
cat << 'EOF'
Usage: ./scripts/check.sh [--format|--no-format]
Options:
--format Apply formatting fixes only (shfmt, gofmt)
--no-format Skip formatting and run checks only
--help Show this help
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--format)
MODE="format"
shift
;;
--no-format)
MODE="check"
shift
;;
--help | -h)
usage
exit 0
;;
*)
echo "Unknown option: $1"
usage
exit 1
;;
esac
done
cd "$PROJECT_ROOT"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
cd "$PROJECT_ROOT"
readonly ICON_SUCCESS="✓"
readonly ICON_ERROR="☻"
readonly ICON_WARNING="●"
readonly ICON_LIST="•"
echo -e "${BLUE}=== Running Mole Quality Checks ===${NC}\n"
echo -e "${BLUE}=== Mole Check (${MODE}) ===${NC}\n"
# 1. Format check
echo -e "${YELLOW}1. Checking code formatting...${NC}"
if command -v shfmt > /dev/null 2>&1; then
if ./scripts/format.sh --check; then
echo -e "${GREEN}✓ Formatting check passed${NC}\n"
SHELL_FILES=$(find . -type f \( -name "*.sh" -o -name "mole" \) \
-not -path "./.git/*" \
-not -path "*/node_modules/*" \
-not -path "*/tests/tmp-*/*" \
-not -path "*/.*" \
2> /dev/null)
if [[ "$MODE" == "format" ]]; then
echo -e "${YELLOW}Formatting shell scripts...${NC}"
if command -v shfmt > /dev/null 2>&1; then
echo "$SHELL_FILES" | xargs shfmt -i 4 -ci -sr -w
echo -e "${GREEN}${ICON_SUCCESS} Shell formatting complete${NC}\n"
else
echo -e "${RED}✗ Formatting check failed${NC}\n"
echo -e "${RED}${ICON_ERROR} shfmt not installed${NC}"
exit 1
fi
else
echo -e "${YELLOW}⚠ shfmt not installed, skipping format check${NC}\n"
if command -v go > /dev/null 2>&1; then
echo -e "${YELLOW}Formatting Go code...${NC}"
gofmt -w ./cmd
echo -e "${GREEN}${ICON_SUCCESS} Go formatting complete${NC}\n"
else
echo -e "${YELLOW}${ICON_WARNING} go not installed, skipping gofmt${NC}\n"
fi
echo -e "${GREEN}=== Format Completed ===${NC}"
exit 0
fi
# 2. ShellCheck
echo -e "${YELLOW}2. Running ShellCheck...${NC}"
if [[ "$MODE" != "check" ]]; then
echo -e "${YELLOW}1. Formatting shell scripts...${NC}"
if command -v shfmt > /dev/null 2>&1; then
echo "$SHELL_FILES" | xargs shfmt -i 4 -ci -sr -w
echo -e "${GREEN}${ICON_SUCCESS} Shell formatting applied${NC}\n"
else
echo -e "${YELLOW}${ICON_WARNING} shfmt not installed, skipping${NC}\n"
fi
if command -v go > /dev/null 2>&1; then
echo -e "${YELLOW}2. Formatting Go code...${NC}"
gofmt -w ./cmd
echo -e "${GREEN}${ICON_SUCCESS} Go formatting applied${NC}\n"
fi
fi
echo -e "${YELLOW}3. Running ShellCheck...${NC}"
if command -v shellcheck > /dev/null 2>&1; then
# Count total files
SHELL_FILES=$(find . -type f \( -name "*.sh" -o -name "mole" \) -not -path "./tests/*" -not -path "./.git/*")
FILE_COUNT=$(echo "$SHELL_FILES" | wc -l | tr -d ' ')
if shellcheck mole bin/*.sh lib/*/*.sh scripts/*.sh 2>&1 | grep -q "SC[0-9]"; then
echo -e "${YELLOW}⚠ ShellCheck found some issues (non-critical):${NC}"
shellcheck mole bin/*.sh lib/*/*.sh scripts/*.sh 2>&1 | head -20
echo -e "${GREEN}✓ ShellCheck completed (${FILE_COUNT} files checked)${NC}\n"
if shellcheck mole bin/*.sh lib/*/*.sh scripts/*.sh; then
echo -e "${GREEN}${ICON_SUCCESS} ShellCheck passed${NC}\n"
else
echo -e "${GREEN}✓ ShellCheck passed (${FILE_COUNT} files checked)${NC}\n"
fi
else
echo -e "${YELLOW}⚠ shellcheck not installed, skipping${NC}\n"
fi
# 3. Unit tests (if available)
echo -e "${YELLOW}3. Running tests...${NC}"
if command -v bats > /dev/null 2>&1 && [ -d "tests" ]; then
if bats tests/*.bats; then
echo -e "${GREEN}✓ Tests passed${NC}\n"
else
echo -e "${RED}✗ Tests failed (see output above)${NC}\n"
echo -e "${RED}${ICON_ERROR} ShellCheck failed${NC}\n"
exit 1
fi
else
echo -e "${YELLOW}⚠ bats not installed or no tests found, skipping${NC}\n"
echo -e "${YELLOW}${ICON_WARNING} shellcheck not installed, skipping${NC}\n"
fi
# 4. Code optimization checks
echo -e "${YELLOW}4. Checking code optimizations...${NC}"
echo -e "${YELLOW}4. Running syntax check...${NC}"
if ! bash -n mole; then
echo -e "${RED}${ICON_ERROR} Syntax check failed (mole)${NC}\n"
exit 1
fi
for script in bin/*.sh; do
if ! bash -n "$script"; then
echo -e "${RED}${ICON_ERROR} Syntax check failed ($script)${NC}\n"
exit 1
fi
done
find lib -name "*.sh" | while read -r script; do
if ! bash -n "$script"; then
echo -e "${RED}${ICON_ERROR} Syntax check failed ($script)${NC}\n"
exit 1
fi
done
echo -e "${GREEN}${ICON_SUCCESS} Syntax check passed${NC}\n"
echo -e "${YELLOW}5. Checking optimizations...${NC}"
OPTIMIZATION_SCORE=0
TOTAL_CHECKS=0
# Check 1: Keyboard input handling (restored to 1s for reliability)
((TOTAL_CHECKS++))
if grep -q "read -r -s -n 1 -t 1" lib/core/ui.sh; then
echo -e "${GREEN} Keyboard timeout properly configured (1s)${NC}"
echo -e "${GREEN} ${ICON_SUCCESS} Keyboard timeout configured${NC}"
((OPTIMIZATION_SCORE++))
else
echo -e "${YELLOW} Keyboard timeout may be misconfigured${NC}"
echo -e "${YELLOW} ${ICON_WARNING} Keyboard timeout may be misconfigured${NC}"
fi
# Check 2: Single-pass drain_pending_input
((TOTAL_CHECKS++))
DRAIN_PASSES=$(grep -c "while IFS= read -r -s -n 1" lib/core/ui.sh 2> /dev/null || true)
DRAIN_PASSES=${DRAIN_PASSES:-0}
if [[ $DRAIN_PASSES -eq 1 ]]; then
echo -e "${GREEN} drain_pending_input optimized (single-pass)${NC}"
echo -e "${GREEN} ${ICON_SUCCESS} drain_pending_input optimized${NC}"
((OPTIMIZATION_SCORE++))
else
echo -e "${YELLOW} drain_pending_input has multiple passes${NC}"
echo -e "${YELLOW} ${ICON_WARNING} drain_pending_input has multiple passes${NC}"
fi
# Check 3: Log rotation once per session
((TOTAL_CHECKS++))
if grep -q "rotate_log_once" lib/core/log.sh; then
echo -e "${GREEN} Log rotation optimized (once per session)${NC}"
echo -e "${GREEN} ${ICON_SUCCESS} Log rotation optimized${NC}"
((OPTIMIZATION_SCORE++))
else
echo -e "${YELLOW} Log rotation not optimized${NC}"
echo -e "${YELLOW} ${ICON_WARNING} Log rotation not optimized${NC}"
fi
# Check 4: Simplified cache validation
((TOTAL_CHECKS++))
if ! grep -q "cache_meta\|cache_dir_mtime" bin/uninstall.sh; then
echo -e "${GREEN} Cache validation simplified${NC}"
echo -e "${GREEN} ${ICON_SUCCESS} Cache validation simplified${NC}"
((OPTIMIZATION_SCORE++))
else
echo -e "${YELLOW} Cache still uses redundant metadata${NC}"
echo -e "${YELLOW} ${ICON_WARNING} Cache still uses redundant metadata${NC}"
fi
# Check 5: Stricter path validation
((TOTAL_CHECKS++))
if grep -q "Consecutive slashes" bin/clean.sh; then
echo -e "${GREEN} Path validation enhanced${NC}"
echo -e "${GREEN} ${ICON_SUCCESS} Path validation enhanced${NC}"
((OPTIMIZATION_SCORE++))
else
echo -e "${YELLOW} Path validation not enhanced${NC}"
echo -e "${YELLOW} ${ICON_WARNING} Path validation not enhanced${NC}"
fi
echo -e "${BLUE} Optimization score: $OPTIMIZATION_SCORE/$TOTAL_CHECKS${NC}\n"
# Summary
echo -e "${GREEN}=== All Checks Completed ===${NC}"
echo -e "${GREEN}=== Checks Completed ===${NC}"
if [[ $OPTIMIZATION_SCORE -eq $TOTAL_CHECKS ]]; then
echo -e "${GREEN}✓ Code quality checks passed!${NC}"
echo -e "${GREEN}✓ All optimizations applied!${NC}"
echo -e "${GREEN}${ICON_SUCCESS} All optimizations applied${NC}"
else
echo -e "${YELLOW}⚠ Code quality checks passed, but some optimizations missing${NC}"
echo -e "${YELLOW}${ICON_WARNING} Some optimizations missing${NC}"
fi

View File

@@ -1,73 +0,0 @@
#!/bin/bash
# Format all shell scripts in the Mole project
#
# Usage:
# ./scripts/format.sh # Format all scripts
# ./scripts/format.sh --check # Check only, don't modify
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
CHECK_ONLY=false
# Parse arguments
if [[ "${1:-}" == "--check" ]]; then
CHECK_ONLY=true
elif [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
cat << 'EOF'
Usage: ./scripts/format.sh [--check]
Format shell scripts using shfmt.
Options:
--check Check formatting without modifying files
--help Show this help
Install: brew install shfmt
EOF
exit 0
fi
# Check if shfmt is installed
if ! command -v shfmt > /dev/null 2>&1; then
echo "Error: shfmt not installed"
echo "Install: brew install shfmt"
exit 1
fi
# Find all shell scripts (excluding temp directories and build artifacts)
cd "$PROJECT_ROOT"
# Build list of files to format (exclude .git, node_modules, tmp directories)
FILES=$(find . -type f \( -name "*.sh" -o -name "mole" \) \
-not -path "./.git/*" \
-not -path "*/node_modules/*" \
-not -path "*/tests/tmp-*/*" \
-not -path "*/.*" \
2> /dev/null)
if [[ -z "$FILES" ]]; then
echo "No shell scripts found"
exit 0
fi
# shfmt options: -i 4 (4 spaces), -ci (indent switch cases), -sr (space after redirect)
if [[ "$CHECK_ONLY" == "true" ]]; then
echo "Checking formatting..."
if echo "$FILES" | xargs shfmt -i 4 -ci -sr -d > /dev/null 2>&1; then
echo "✓ All scripts properly formatted"
exit 0
else
echo "✗ Some scripts need formatting:"
echo "$FILES" | xargs shfmt -i 4 -ci -sr -d
echo ""
echo "Run './scripts/format.sh' to fix"
exit 1
fi
else
echo "Formatting scripts..."
echo "$FILES" | xargs shfmt -i 4 -ci -sr -w
echo "✓ Done"
fi

View File

@@ -1,133 +0,0 @@
#!/bin/bash
# Quick test runner script
# Runs all tests before committing
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/.."
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo "==============================="
echo "Mole Test Runner"
echo "==============================="
echo ""
# Track failures
FAILED=0
# 1. ShellCheck
echo "1. Running ShellCheck..."
if command -v shellcheck > /dev/null 2>&1; then
# Optimize: Collect all files first, then pass to shellcheck in one call
SHELL_FILES=()
while IFS= read -r file; do
SHELL_FILES+=("$file")
done < <(find lib -name "*.sh" -type f)
if shellcheck mole bin/*.sh "${SHELL_FILES[@]}" 2> /dev/null; then
printf "${GREEN}✓ ShellCheck passed${NC}\n"
else
printf "${RED}✗ ShellCheck failed${NC}\n"
((FAILED++))
fi
else
printf "${YELLOW}⚠ ShellCheck not installed, skipping${NC}\n"
fi
echo ""
# 2. Syntax Check
echo "2. Running syntax check..."
SYNTAX_OK=true
# Check main file
bash -n mole 2> /dev/null || SYNTAX_OK=false
# Check all shell files without requiring bash 4+
# Note: bash -n must check files one-by-one (can't batch process)
if [[ "$SYNTAX_OK" == "true" ]]; then
while IFS= read -r file; do
bash -n "$file" 2> /dev/null || {
SYNTAX_OK=false
break
}
done < <(find bin lib -name "*.sh" -type f)
fi
if [[ "$SYNTAX_OK" == "true" ]]; then
printf "${GREEN}✓ Syntax check passed${NC}\n"
else
printf "${RED}✗ Syntax check failed${NC}\n"
((FAILED++))
fi
echo ""
# 3. Unit Tests
echo "3. Running unit tests..."
if command -v bats > /dev/null 2>&1; then
# Note: bats might detect non-TTY and suppress color.
# Adding --tap prevents spinner issues in background.
if bats tests/*.bats; then
printf "${GREEN}✓ Unit tests passed${NC}\n"
else
printf "${RED}✗ Unit tests failed${NC}\n"
((FAILED++))
fi
else
printf "${YELLOW}⚠ Bats not installed, skipping unit tests${NC}\n"
echo " Install with: brew install bats-core"
fi
echo ""
# 4. Go Tests
echo "4. Running Go tests..."
if command -v go > /dev/null 2>&1; then
if go build ./... && go vet ./cmd/... && go test ./cmd/...; then
printf "${GREEN}✓ Go tests passed${NC}\n"
else
printf "${RED}✗ Go tests failed${NC}\n"
((FAILED++))
fi
else
printf "${YELLOW}⚠ Go not installed, skipping Go tests${NC}\n"
fi
echo ""
# 5. Module Loading Test
echo "5. Testing module loading..."
if bash -c 'source lib/core/common.sh && echo "OK"' > /dev/null 2>&1; then
printf "${GREEN}✓ Module loading passed${NC}\n"
else
printf "${RED}✗ Module loading failed${NC}\n"
((FAILED++))
fi
echo ""
# 6. Integration Tests
echo "6. Running integration tests..."
export MOLE_MAX_PARALLEL_JOBS=30
if ./bin/clean.sh --dry-run > /dev/null 2>&1; then
printf "${GREEN}✓ Clean dry-run passed${NC}\n"
else
printf "${RED}✗ Clean dry-run failed${NC}\n"
((FAILED++))
fi
echo ""
# Summary
echo "==============================="
if [[ $FAILED -eq 0 ]]; then
printf "${GREEN}All tests passed!${NC}\n"
echo ""
echo "You can now commit your changes."
exit 0
else
printf "${RED}$FAILED test(s) failed!${NC}\n"
echo ""
echo "Please fix the failing tests before committing."
exit 1
fi

135
scripts/test.sh Executable file
View File

@@ -0,0 +1,135 @@
#!/bin/bash
# Test runner for Mole.
# Runs unit, Go, and integration tests.
# Exits non-zero on failures.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$PROJECT_ROOT"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
readonly ICON_SUCCESS="✓"
readonly ICON_ERROR="☻"
readonly ICON_WARNING="●"
readonly ICON_LIST="•"
echo "==============================="
echo "Mole Test Runner"
echo "==============================="
echo ""
FAILED=0
echo "1. Linting test scripts..."
if command -v shellcheck > /dev/null 2>&1; then
TEST_FILES=()
while IFS= read -r file; do
TEST_FILES+=("$file")
done < <(find tests -type f \( -name '*.bats' -o -name '*.sh' \) | sort)
if [[ ${#TEST_FILES[@]} -gt 0 ]]; then
if shellcheck --rcfile "$PROJECT_ROOT/.shellcheckrc" "${TEST_FILES[@]}"; then
printf "${GREEN}${ICON_SUCCESS} Test script lint passed${NC}\n"
else
printf "${RED}${ICON_ERROR} Test script lint failed${NC}\n"
((FAILED++))
fi
else
printf "${YELLOW}${ICON_WARNING} No test scripts found, skipping${NC}\n"
fi
else
printf "${YELLOW}${ICON_WARNING} shellcheck not installed, skipping${NC}\n"
fi
echo ""
echo "2. Running unit tests..."
if command -v bats > /dev/null 2>&1 && [ -d "tests" ]; then
if [[ -z "${TERM:-}" ]]; then
export TERM="xterm-256color"
fi
if [[ $# -eq 0 ]]; then
set -- tests
fi
if [[ -t 1 ]]; then
if bats -p "$@"; then
printf "${GREEN}${ICON_SUCCESS} Unit tests passed${NC}\n"
else
printf "${RED}${ICON_ERROR} Unit tests failed${NC}\n"
((FAILED++))
fi
else
if TERM="${TERM:-xterm-256color}" bats --tap "$@"; then
printf "${GREEN}${ICON_SUCCESS} Unit tests passed${NC}\n"
else
printf "${RED}${ICON_ERROR} Unit tests failed${NC}\n"
((FAILED++))
fi
fi
else
printf "${YELLOW}${ICON_WARNING} bats not installed or no tests found, skipping${NC}\n"
fi
echo ""
echo "3. Running Go tests..."
if command -v go > /dev/null 2>&1; then
if go build ./... > /dev/null 2>&1 && go vet ./cmd/... > /dev/null 2>&1 && go test ./cmd/... > /dev/null 2>&1; then
printf "${GREEN}${ICON_SUCCESS} Go tests passed${NC}\n"
else
printf "${RED}${ICON_ERROR} Go tests failed${NC}\n"
((FAILED++))
fi
else
printf "${YELLOW}${ICON_WARNING} Go not installed, skipping Go tests${NC}\n"
fi
echo ""
echo "4. Testing module loading..."
if bash -c 'source lib/core/common.sh && echo "OK"' > /dev/null 2>&1; then
printf "${GREEN}${ICON_SUCCESS} Module loading passed${NC}\n"
else
printf "${RED}${ICON_ERROR} Module loading failed${NC}\n"
((FAILED++))
fi
echo ""
echo "5. Running integration tests..."
# Quick syntax check for main scripts
if bash -n mole && bash -n bin/clean.sh && bash -n bin/optimize.sh; then
printf "${GREEN}${ICON_SUCCESS} Integration tests passed${NC}\n"
else
printf "${RED}${ICON_ERROR} Integration tests failed${NC}\n"
((FAILED++))
fi
echo ""
echo "6. Testing installation..."
# Skip if Homebrew mole is installed (install.sh will refuse to overwrite)
if brew list mole &> /dev/null; then
printf "${GREEN}${ICON_SUCCESS} Installation test skipped (Homebrew)${NC}\n"
elif ./install.sh --prefix /tmp/mole-test > /dev/null 2>&1; then
if [ -f /tmp/mole-test/mole ]; then
printf "${GREEN}${ICON_SUCCESS} Installation test passed${NC}\n"
else
printf "${RED}${ICON_ERROR} Installation test failed${NC}\n"
((FAILED++))
fi
else
printf "${RED}${ICON_ERROR} Installation test failed${NC}\n"
((FAILED++))
fi
rm -rf /tmp/mole-test
echo ""
echo "==============================="
if [[ $FAILED -eq 0 ]]; then
printf "${GREEN}${ICON_SUCCESS} All tests passed!${NC}\n"
exit 0
fi
printf "${RED}${ICON_ERROR} $FAILED test(s) failed!${NC}\n"
exit 1