From b950c610d4e41ee6bbfc9476020546548c20100d Mon Sep 17 00:00:00 2001 From: Yehonal Date: Sat, 27 Sep 2025 13:36:14 +0200 Subject: feat(bash): test command in dashboard + fix tests (#23030) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/CODEOWNERS | 2 + .github/workflows/dashboard-ci.yml | 8 +- apps/bash_shared/common.sh | 1 + apps/bash_shared/defines.sh | 8 +- apps/bash_shared/includes.sh | 9 +- apps/compiler/includes/functions.sh | 19 +- apps/installer/main.sh | 4 + apps/test-framework/README.md | 173 +++++++++--- apps/test-framework/run-bash-tests.sh | 314 +++++++++++++++++++++ apps/test-framework/run-core-tests.sh | 17 ++ apps/test-framework/run-tests.sh | 314 --------------------- apps/test-framework/test-main.sh | 45 +++ conf/dist/config.sh | 9 + src/test/mocks/WorldMock.h | 2 +- .../ArenaSeasonRewardDistributorTest.cpp | 16 ++ .../ArenaSeason/ArenaTeamFilterTest.cpp | 12 + 16 files changed, 581 insertions(+), 372 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100755 apps/test-framework/run-bash-tests.sh create mode 100644 apps/test-framework/run-core-tests.sh delete mode 100755 apps/test-framework/run-tests.sh create mode 100644 apps/test-framework/test-main.sh diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..8cff11ccb3 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Protect dashboard workflow – require explicit review +.github/workflows/dashboard-ci.yml @Yehonal diff --git a/.github/workflows/dashboard-ci.yml b/.github/workflows/dashboard-ci.yml index 5901d19447..1bbad51c39 100644 --- a/.github/workflows/dashboard-ci.yml +++ b/.github/workflows/dashboard-ci.yml @@ -54,8 +54,7 @@ jobs: env: TERM: xterm-256color run: | - cd apps/test-framework - ./run-tests.sh --tap --all + ./acore.sh test bash --tap --all build-and-test: name: Build and Integration Test @@ -79,6 +78,7 @@ jobs: cp conf/dist/config.sh conf/config.sh # Configure dashboard sed -i 's/MTHREADS=.*/MTHREADS="4"/' conf/config.sh + sed -i 's/CBUILD_TESTING=.*/CBUILD_TESTING="ON"/' conf/config.sh - name: Test module commands run: | @@ -105,6 +105,10 @@ jobs: ./acore.sh module remove mod-duel-reset ./acore.sh module list + - name: Run core tests + run: | + ./acore.sh test core + - name: Test authserver dry-run run: | cd env/dist/bin diff --git a/apps/bash_shared/common.sh b/apps/bash_shared/common.sh index 027a49bed3..acd23eacdf 100644 --- a/apps/bash_shared/common.sh +++ b/apps/bash_shared/common.sh @@ -1,6 +1,7 @@ function registerHooks() { acore_event_registerHooks "$@"; } function runHooks() { acore_event_runHooks "$@"; } +#shellcheck source=../../conf/dist/config.sh source "$AC_PATH_CONF/dist/config.sh" # include dist to avoid missing conf variables # first check if it's defined in env, otherwise use the default diff --git a/apps/bash_shared/defines.sh b/apps/bash_shared/defines.sh index 4f5372215c..4b014bd9c1 100644 --- a/apps/bash_shared/defines.sh +++ b/apps/bash_shared/defines.sh @@ -19,10 +19,10 @@ case $AC_PATH_ROOT in *) AC_PATH_ROOT=$PWD/$AC_PATH_ROOT;; esac -AC_PATH_CONF="$AC_PATH_ROOT/conf" +export AC_PATH_CONF="$AC_PATH_ROOT/conf" -AC_PATH_MODULES="$AC_PATH_ROOT/modules" +export AC_PATH_MODULES="$AC_PATH_ROOT/modules" -AC_PATH_DEPS="$AC_PATH_ROOT/deps" +export AC_PATH_DEPS="$AC_PATH_ROOT/deps" -AC_PATH_VAR="$AC_PATH_ROOT/var" +export AC_PATH_VAR="$AC_PATH_ROOT/var" diff --git a/apps/bash_shared/includes.sh b/apps/bash_shared/includes.sh index 63593e4c56..d2bf07db12 100644 --- a/apps/bash_shared/includes.sh +++ b/apps/bash_shared/includes.sh @@ -7,10 +7,17 @@ AC_PATH_APPS="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../" && pwd )" AC_PATH_SHARED="$AC_PATH_APPS/bash_shared" +# shellcheck source=./defines.sh source "$AC_PATH_SHARED/defines.sh" +# shellcheck source=../../deps/acore/bash-lib/src/event/hooks.sh source "$AC_PATH_DEPS/acore/bash-lib/src/event/hooks.sh" +# shellcheck source=./common.sh source "$AC_PATH_SHARED/common.sh" -[[ "$OSTYPE" = "msys" ]] && AC_BINPATH_FULL="$BINPATH" || AC_BINPATH_FULL="$BINPATH/bin" +if [[ "$OSTYPE" = "msys" ]]; then + AC_BINPATH_FULL="$BINPATH" +else + export AC_BINPATH_FULL="$BINPATH/bin" +fi diff --git a/apps/compiler/includes/functions.sh b/apps/compiler/includes/functions.sh index 5f9581e7ce..c608cff24d 100644 --- a/apps/compiler/includes/functions.sh +++ b/apps/compiler/includes/functions.sh @@ -1,7 +1,14 @@ #!/usr/bin/env bash # Set SUDO variable - one liner -SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "") +SUDO="" + +IS_SUDO_ENABLED=${AC_ENABLE_ROOT_CMAKE_INSTALL:-0} + +# Allow callers to opt-out from privilege escalation during install/perms adjustments +if [[ $IS_SUDO_ENABLED == 1 ]]; then + SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "") +fi function comp_clean() { DIRTOCLEAN=${BUILDPATH:-var/build/obj} @@ -143,9 +150,13 @@ function comp_compile() { popd >> /dev/null || exit 1 # set all aplications SUID bit - echo "Setting permissions on binary files" - find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chown root:root -- {} + - find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chmod u+s -- {} + + if [[ $IS_SUDO_ENABLED == 0 ]]; then + echo "Skipping root ownership and SUID changes (IS_SUDO_ENABLED=0)" + else + echo "Setting permissions on binary files" + find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chown root:root -- {} + + find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chmod u+s -- {} + + fi [[ -f "$confDir/worldserver.conf.dist" ]] && \ cp -v --no-clobber "$confDir/worldserver.conf.dist" "$confDir/worldserver.conf" diff --git a/apps/installer/main.sh b/apps/installer/main.sh index b0772f76cf..fea9dc3acf 100644 --- a/apps/installer/main.sh +++ b/apps/installer/main.sh @@ -41,6 +41,7 @@ menu_items=( "client-data|gd|download client data from github repository (beta)" "run-worldserver|rw|execute a simple restarter for worldserver" "run-authserver|ra|execute a simple restarter for authserver" + "test|t|Run test framework" "docker|dr|Run docker tools" "version|v|Show AzerothCore version" "service-manager|sm|Run service manager to run authserver and worldserver in background" @@ -84,6 +85,9 @@ function handle_menu_command() { "run-authserver") inst_simple_restarter authserver ;; + "test") + bash "$AC_PATH_APPS/test-framework/test-main.sh" "$@" + ;; "docker") DOCKER=1 bash "$AC_PATH_ROOT/apps/docker/docker-cmd.sh" "$@" exit diff --git a/apps/test-framework/README.md b/apps/test-framework/README.md index dda5237067..61511bc364 100644 --- a/apps/test-framework/README.md +++ b/apps/test-framework/README.md @@ -6,7 +6,9 @@ This is the centralized test framework for all AzerothCore bash scripts. It prov ``` apps/test-framework/ -├── run-tests.sh # Universal test runner (single entry point) +├── test-main.sh # Unified test framework entry point +├── run-bash-tests.sh # Bash test runner for BATS tests +├── run-core-tests.sh # AzerothCore unit test runner ├── README.md # This documentation ├── bats_libs/ # Custom BATS libraries │ ├── acore-support.bash # Test setup and helpers @@ -17,38 +19,74 @@ apps/test-framework/ ## Quick Start +### Using acore.sh (Recommended): +```bash +# Run the unified test framework (interactive menu) +./acore.sh test + +# Run bash tests directly +./acore.sh test bash --all + +# Run AzerothCore unit tests +./acore.sh test core +``` + ### From any module directory: ```bash # Run tests for current module -../test-framework/run-tests.sh --dir . +../test-framework/run-bash-tests.sh --dir . ``` ### From test-framework directory: ```bash # Run all tests in all modules -./run-tests.sh --all +./run-bash-tests.sh --all # Run tests for specific module -./run-tests.sh startup-scripts +./run-bash-tests.sh startup-scripts # List available modules -./run-tests.sh --list +./run-bash-tests.sh --list # Run tests with debug info -./run-tests.sh --all --debug +./run-bash-tests.sh --all --debug ``` ### From project root: ```bash # Run all tests -apps/test-framework/run-tests.sh --all +apps/test-framework/run-bash-tests.sh --all # Run specific module -apps/test-framework/run-tests.sh startup-scripts +apps/test-framework/run-bash-tests.sh startup-scripts # Run with verbose output -apps/test-framework/run-tests.sh startup-scripts --verbose +apps/test-framework/run-bash-tests.sh startup-scripts --verbose +``` + +## Test Types + +The framework now supports two types of tests: + +1. **Bash Tests** - BATS-based tests for bash scripts and functionality +2. **Core Tests** - AzerothCore C++ unit tests + +### Unified Test Framework + +The test framework provides a unified entry point through `test-main.sh` which presents an interactive menu: + +- **bash**: Run BATS-based bash script tests +- **core**: Run AzerothCore C++ unit tests +- **quit**: Exit the test framework + +```bash +# Interactive test menu +./acore.sh test + +# Direct test execution +./acore.sh test bash --all # Run all bash tests +./acore.sh test core # Run core unit tests ``` ## Usage @@ -57,35 +95,35 @@ apps/test-framework/run-tests.sh startup-scripts --verbose ```bash # Run all tests -./run-tests.sh --all +./run-bash-tests.sh --all # Run tests for specific module -./run-tests.sh startup-scripts +./run-bash-tests.sh startup-scripts # Run tests matching pattern -./run-tests.sh --filter starter +./run-bash-tests.sh --filter starter # Run tests in specific directory -./run-tests.sh --dir apps/docker +./run-bash-tests.sh --dir apps/docker # Show available modules -./run-tests.sh --list +./run-bash-tests.sh --list # Show test count -./run-tests.sh --count +./run-bash-tests.sh --count ``` ### Output Formats ```bash # Pretty output (default) -./run-tests.sh --pretty +./run-bash-tests.sh --pretty # TAP output for CI/CD -./run-tests.sh --tap +./run-bash-tests.sh --tap # Verbose output with debug info -./run-tests.sh --verbose --debug +./run-bash-tests.sh --verbose --debug ``` ## Writing Tests @@ -205,17 +243,17 @@ debug_on_failure From your module directory: ```bash -../test-framework/run-tests.sh --dir . +../test-framework/run-bash-tests.sh --dir . ``` From the test framework: ```bash -./run-tests.sh my-module +./run-bash-tests.sh my-module ``` From project root: ```bash -apps/test-framework/run-tests.sh my-module +apps/test-framework/run-bash-tests.sh my-module ``` ## CI/CD Integration @@ -223,37 +261,73 @@ apps/test-framework/run-tests.sh my-module For continuous integration, use TAP output: ```bash -# In your CI script +# Recommended: Use acore.sh integration +./acore.sh test bash --tap --all > test-results.tap + +# Direct script usage cd apps/test-framework -./run-tests.sh --all --tap > test-results.tap +./run-bash-tests.sh --all --tap > test-results.tap # Or from project root -apps/test-framework/run-tests.sh --all --tap > test-results.tap +apps/test-framework/run-bash-tests.sh --all --tap > test-results.tap + +# Run core unit tests in CI +./acore.sh test core +``` + +## Core Tests + +The framework now includes support for AzerothCore's C++ unit tests through `run-core-tests.sh`: + +```bash +# Run core unit tests +./acore.sh test core + +# Direct script usage +apps/test-framework/run-core-tests.sh ``` +**Prerequisites for Core Tests:** +- Project must be built with unit tests enabled (`CBUILD_TESTING="ON"` inside `conf/config.sh` that works with the acore.sh compiler) +- Unit test binary should be available at `$BUILDPATH/src/test/unit_tests` + +The core test runner will: +1. Check if the unit test binary exists +2. Execute the AzerothCore unit tests +3. Return appropriate exit codes for CI/CD integration + ## Available Commands -All functionality is available through the single `run-tests.sh` script: +### Unified Test Framework Commands + +Recommended usage through `acore.sh`: +- `./acore.sh test` - Interactive test framework menu +- `./acore.sh test bash [options]` - Run bash tests with options +- `./acore.sh test core` - Run AzerothCore unit tests + +### Bash Test Commands + +All bash test functionality is available through the `run-bash-tests.sh` script: ### Basic Test Execution -- `./run-tests.sh --all` - Run all tests in all modules -- `./run-tests.sh ` - Run tests for specific module -- `./run-tests.sh --dir ` - Run tests in specific directory -- `./run-tests.sh --list` - List available modules -- `./run-tests.sh --count` - Show test count +- `./run-bash-tests.sh --all` - Run all tests in all modules +- `./run-bash-tests.sh ` - Run tests for specific module +- `./run-bash-tests.sh --dir ` - Run tests in specific directory +- `./run-bash-tests.sh --list` - List available modules +- `./run-bash-tests.sh --count` - Show test count ### Output Control -- `./run-tests.sh --verbose` - Verbose output with debug info -- `./run-tests.sh --tap` - TAP output for CI/CD -- `./run-tests.sh --debug` - Debug mode with failure details -- `./run-tests.sh --pretty` - Pretty output (default) +- `./run-bash-tests.sh --verbose` - Verbose output with debug info +- `./run-bash-tests.sh --tap` - TAP output for CI/CD +- `./run-bash-tests.sh --debug` - Debug mode with failure details +- `./run-bash-tests.sh --pretty` - Pretty output (default) ### Test Filtering -- `./run-tests.sh --filter ` - Run tests matching pattern -- `./run-tests.sh --filter ` - Filter within module +- `./run-bash-tests.sh --filter ` - Run tests matching pattern +- `./run-bash-tests.sh --filter ` - Filter within module ### Utility Functions -- `./run-tests.sh --help` - Show help message +- `./run-bash-tests.sh --help` - Show help message - Install BATS: Use your system package manager (`apt install bats`, `brew install bats-core`, etc.) @@ -264,35 +338,42 @@ All functionality is available through the single `run-tests.sh` script: ### Running Specific Tests ```bash # Run only starter-related tests -./run-tests.sh --filter starter +./run-bash-tests.sh --filter starter # Run only tests in startup-scripts module -./run-tests.sh startup-scripts +./run-bash-tests.sh startup-scripts # Run all tests with verbose output -./run-tests.sh --all --verbose +./run-bash-tests.sh --all --verbose # Run tests in specific directory with debug -./run-tests.sh --dir apps/docker --debug +./run-bash-tests.sh --dir apps/docker --debug ``` ### Development Workflow ```bash +# Recommended: Use acore.sh for unified testing +./acore.sh test # Interactive menu +./acore.sh test bash --all # All bash tests +./acore.sh test core # Core unit tests + # While developing, run tests frequently from module directory cd apps/my-module -../test-framework/run-tests.sh --dir . +../test-framework/run-bash-tests.sh --dir . # Debug failing tests -../test-framework/run-tests.sh --dir . --debug --verbose +../test-framework/run-bash-tests.sh --dir . --debug --verbose # Run specific test pattern -../test-framework/run-tests.sh --dir . --filter my-feature +../test-framework/run-bash-tests.sh --dir . --filter my-feature # From project root - run all tests -apps/test-framework/run-tests.sh --all +./acore.sh test bash --all # Recommended +apps/test-framework/run-bash-tests.sh --all # Direct # Quick test count check -apps/test-framework/run-tests.sh --count +./acore.sh test bash --count # Recommended +apps/test-framework/run-bash-tests.sh --count # Direct ``` ## Benefits diff --git a/apps/test-framework/run-bash-tests.sh b/apps/test-framework/run-bash-tests.sh new file mode 100755 index 0000000000..ce624a95a2 --- /dev/null +++ b/apps/test-framework/run-bash-tests.sh @@ -0,0 +1,314 @@ +#!/usr/bin/env bash + +# AzerothCore Universal Test Runner +# This script provides a unified way to run BATS tests across all modules + +# Get the script directory and project root +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +# Count cores for parallel execution +if [[ -z "$ACORE_TEST_CORES" ]]; then + if command -v nproc >/dev/null 2>&1; then + ACORE_TEST_CORES=$(nproc) + elif command -v sysctl >/dev/null 2>&1; then + ACORE_TEST_CORES=$(sysctl -n hw.ncpu) + else + ACORE_TEST_CORES=1 # Fallback to single core if detection fails + fi + export ACORE_TEST_CORES +fi + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +show_help() { + echo -e "${BLUE}AzerothCore Universal Test Runner${NC}" + echo "" + echo "Usage: $0 [OPTIONS] [TEST_MODULES...]" + echo "" + echo "Options:" + echo " -h, --help Show this help message" + echo " -v, --verbose Enable verbose output" + echo " -t, --tap Use TAP output format (for CI/CD)" + echo " -p, --pretty Use pretty output format (default)" + echo " -f, --filter Run only tests matching pattern" + echo " -c, --count Show test count only" + echo " -d, --debug Enable debug mode (shows output on failure)" + echo " -l, --list List available test modules" + echo " -j, --jobs Set number of parallel jobs (default: $ACORE_TEST_CORES)" + echo " --dir Run tests in specific directory" + echo " --all Run all tests in all modules" + echo "" + echo "Test Modules:" + echo " startup-scripts - Startup script tests" + echo " compiler - Compiler script tests" + echo " docker - Docker-related tests" + echo " installer - Installer script tests" + echo "" + echo "Examples:" + echo " $0 # Run tests in current directory" + echo " $0 --all # Run all tests in all modules" + echo " $0 startup-scripts # Run startup-scripts tests only" + echo " $0 --dir apps/docker # Run tests in specific directory" + echo " $0 --verbose startup-scripts # Run with verbose output" + echo " $0 --filter starter # Run only tests matching 'starter'" + echo " $0 --tap # Output in TAP format for CI" +} + +# Parse command line arguments +VERBOSE=false +TAP=false +PRETTY=true +FILTER="" +COUNT_ONLY=false +DEBUG=false +LIST_MODULES=false +RUN_ALL=false +TEST_DIRS=() +TEST_MODULES=() + +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_help + exit 0 + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + -t|--tap) + TAP=true + PRETTY=false + shift + ;; + -p|--pretty) + PRETTY=true + TAP=false + shift + ;; + -f|--filter) + FILTER="$2" + shift 2 + ;; + -c|--count) + COUNT_ONLY=true + shift + ;; + -d|--debug) + DEBUG=true + shift + ;; + -l|--list) + LIST_MODULES=true + shift + ;; + --dir) + TEST_DIRS+=("$2") + shift 2 + ;; + --all) + RUN_ALL=true + shift + ;; + -j|--jobs) + if [[ "$2" =~ ^[0-9]+$ ]]; then + ACORE_TEST_CORES="$2" + export ACORE_TEST_CORES + shift 2 + else + echo -e "${RED}Error: Invalid number of jobs specified: $2${NC}" + echo "Please provide a valid number." + exit 1 + fi + ;; + *.bats) + # Individual test files + TEST_FILES+=("$1") + shift + ;; + *) + # Assume it's a module name + TEST_MODULES+=("$1") + shift + ;; + esac +done + +# Check if BATS is installed +if ! command -v bats >/dev/null 2>&1; then + echo -e "${RED}Error: BATS is not installed${NC}" + echo "Please install BATS first:" + echo " sudo apt install bats # On Ubuntu/Debian" + echo " brew install bats-core # On macOS" + echo "Or run: make install-bats" + exit 1 +fi + +# Function to find test directories +find_test_directories() { + local search_paths=() + + if [[ "$RUN_ALL" == true ]]; then + # Find all test directories + mapfile -t search_paths < <(find "$PROJECT_ROOT/apps" -type d -name "test" 2>/dev/null) + elif [[ ${#TEST_DIRS[@]} -gt 0 ]]; then + # Use specified directories + for dir in "${TEST_DIRS[@]}"; do + if [[ -d "$PROJECT_ROOT/$dir/test" ]]; then + search_paths+=("$PROJECT_ROOT/$dir/test") + elif [[ -d "$dir/test" ]]; then + search_paths+=("$dir/test") + elif [[ -d "$dir" ]]; then + search_paths+=("$dir") + else + echo -e "${YELLOW}Warning: Test directory not found: $dir${NC}" + fi + done + elif [[ ${#TEST_MODULES[@]} -gt 0 ]]; then + # Use specified modules + for module in "${TEST_MODULES[@]}"; do + if [[ -d "$PROJECT_ROOT/apps/$module/test" ]]; then + search_paths+=("$PROJECT_ROOT/apps/$module/test") + else + echo -e "${YELLOW}Warning: Module test directory not found: $module${NC}" + fi + done + else + # Default: use current directory or startup-scripts if run from test-framework + if [[ "$(basename "$PWD")" == "test-framework" ]]; then + search_paths=("$PROJECT_ROOT/apps/startup-scripts/test") + elif [[ -d "./test" ]]; then + search_paths=("./test") + else + echo -e "${YELLOW}No test directory found. Use --all or specify a module.${NC}" + exit 0 + fi + fi + + echo "${search_paths[@]}" +} + +# Function to list available modules +list_modules() { + echo -e "${BLUE}Available test modules:${NC}" + find "$PROJECT_ROOT/apps" -type d -name "test" 2>/dev/null | while read -r test_dir; do + module_name=$(basename "$(dirname "$test_dir")") + test_count=$(find "$test_dir" -name "*.bats" | wc -l) + echo -e " ${GREEN}$module_name${NC} ($test_count test files)" + done +} + +# Show available modules if requested +if [[ "$LIST_MODULES" == true ]]; then + list_modules + exit 0 +fi + +# Find test directories +TEST_SEARCH_PATHS=($(find_test_directories)) + +if [[ ${#TEST_SEARCH_PATHS[@]} -eq 0 ]]; then + echo -e "${YELLOW}No test directories found.${NC}" + echo "Use --list to see available modules." + exit 0 +fi + +# Collect all test files +TEST_FILES=() +for test_dir in "${TEST_SEARCH_PATHS[@]}"; do + if [[ -d "$test_dir" ]]; then + if [[ -n "$FILTER" ]]; then + # Find test files matching filter + mapfile -t filtered_files < <(find "$test_dir" -name "*.bats" -exec grep -l "$FILTER" {} \; 2>/dev/null) + TEST_FILES+=("${filtered_files[@]}") + else + # Use all test files in directory + mapfile -t dir_files < <(find "$test_dir" -name "*.bats" 2>/dev/null) + TEST_FILES+=("${dir_files[@]}") + fi + fi +done + +if [[ ${#TEST_FILES[@]} -eq 0 ]]; then + if [[ -n "$FILTER" ]]; then + echo -e "${YELLOW}No test files found matching filter: $FILTER${NC}" + else + echo -e "${YELLOW}No test files found in specified directories.${NC}" + fi + exit 0 +fi + +# Show test count only +if [[ "$COUNT_ONLY" == true ]]; then + total_tests=0 + for file in "${TEST_FILES[@]}"; do + count=$(grep -c "^@test" "$file" 2>/dev/null || echo 0) + total_tests=$((total_tests + count)) + done + echo "Total tests: $total_tests" + echo "Test files: ${#TEST_FILES[@]}" + echo "Test directories: ${#TEST_SEARCH_PATHS[@]}" + exit 0 +fi + +# Build BATS command +BATS_CMD="bats --jobs $ACORE_TEST_CORES" + +# Set output format +if [[ "$TAP" == true ]]; then + BATS_CMD+=" --formatter tap" +elif [[ "$PRETTY" == true ]]; then + BATS_CMD+=" --formatter pretty" +fi + +# Enable verbose output +if [[ "$VERBOSE" == true ]]; then + BATS_CMD+=" --verbose-run" +fi + +# Add filter if specified +if [[ -n "$FILTER" ]]; then + BATS_CMD+=" --filter '$FILTER'" +fi + +# Add test files +BATS_CMD+=" ${TEST_FILES[*]}" + +echo -e "${BLUE}Running AzerothCore Tests with ${ACORE_TEST_CORES} jobs${NC}" +echo -e "${YELLOW}Test directories: ${TEST_SEARCH_PATHS[*]}${NC}" +echo -e "${YELLOW}Test files: ${#TEST_FILES[@]}${NC}" +if [[ -n "$FILTER" ]]; then + echo -e "${YELLOW}Filter: $FILTER${NC}" +fi +echo "" + +# Run tests +if [[ "$DEBUG" == true ]]; then + echo -e "${YELLOW}Command: $BATS_CMD${NC}" + echo "" +fi + +# Execute BATS +if eval "$BATS_CMD"; then + echo "" + echo -e "${GREEN}✅ All tests passed!${NC}" + exit 0 +else + exit_code=$? + echo "" + echo -e "${RED}❌ Some tests failed!${NC}" + + if [[ "$DEBUG" == true ]]; then + echo -e "${YELLOW}Tip: Check the output above for detailed error information${NC}" + echo -e "${YELLOW}You can also run individual tests for more detailed debugging:${NC}" + echo -e "${YELLOW} $0 --verbose --filter ${NC}" + fi + + exit $exit_code +fi diff --git a/apps/test-framework/run-core-tests.sh b/apps/test-framework/run-core-tests.sh new file mode 100644 index 0000000000..0f72fd572f --- /dev/null +++ b/apps/test-framework/run-core-tests.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + + # shellcheck source-path=SCRIPTDIR +CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# shellcheck source=../bash_shared/includes.sh +source "$CURRENT_PATH/../bash_shared/includes.sh" + +TEST_PATH="$BUILDPATH/src/test/unit_tests" + +if [[ ! -f "$TEST_PATH" ]]; then + echo "Unit test binary not found at $TEST_PATH" + echo "Please ensure the project is built with unit tests enabled." + exit 1 +fi + +exec "$TEST_PATH" "$@" \ No newline at end of file diff --git a/apps/test-framework/run-tests.sh b/apps/test-framework/run-tests.sh deleted file mode 100755 index ce624a95a2..0000000000 --- a/apps/test-framework/run-tests.sh +++ /dev/null @@ -1,314 +0,0 @@ -#!/usr/bin/env bash - -# AzerothCore Universal Test Runner -# This script provides a unified way to run BATS tests across all modules - -# Get the script directory and project root -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" - -# Count cores for parallel execution -if [[ -z "$ACORE_TEST_CORES" ]]; then - if command -v nproc >/dev/null 2>&1; then - ACORE_TEST_CORES=$(nproc) - elif command -v sysctl >/dev/null 2>&1; then - ACORE_TEST_CORES=$(sysctl -n hw.ncpu) - else - ACORE_TEST_CORES=1 # Fallback to single core if detection fails - fi - export ACORE_TEST_CORES -fi - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -show_help() { - echo -e "${BLUE}AzerothCore Universal Test Runner${NC}" - echo "" - echo "Usage: $0 [OPTIONS] [TEST_MODULES...]" - echo "" - echo "Options:" - echo " -h, --help Show this help message" - echo " -v, --verbose Enable verbose output" - echo " -t, --tap Use TAP output format (for CI/CD)" - echo " -p, --pretty Use pretty output format (default)" - echo " -f, --filter Run only tests matching pattern" - echo " -c, --count Show test count only" - echo " -d, --debug Enable debug mode (shows output on failure)" - echo " -l, --list List available test modules" - echo " -j, --jobs Set number of parallel jobs (default: $ACORE_TEST_CORES)" - echo " --dir Run tests in specific directory" - echo " --all Run all tests in all modules" - echo "" - echo "Test Modules:" - echo " startup-scripts - Startup script tests" - echo " compiler - Compiler script tests" - echo " docker - Docker-related tests" - echo " installer - Installer script tests" - echo "" - echo "Examples:" - echo " $0 # Run tests in current directory" - echo " $0 --all # Run all tests in all modules" - echo " $0 startup-scripts # Run startup-scripts tests only" - echo " $0 --dir apps/docker # Run tests in specific directory" - echo " $0 --verbose startup-scripts # Run with verbose output" - echo " $0 --filter starter # Run only tests matching 'starter'" - echo " $0 --tap # Output in TAP format for CI" -} - -# Parse command line arguments -VERBOSE=false -TAP=false -PRETTY=true -FILTER="" -COUNT_ONLY=false -DEBUG=false -LIST_MODULES=false -RUN_ALL=false -TEST_DIRS=() -TEST_MODULES=() - -while [[ $# -gt 0 ]]; do - case $1 in - -h|--help) - show_help - exit 0 - ;; - -v|--verbose) - VERBOSE=true - shift - ;; - -t|--tap) - TAP=true - PRETTY=false - shift - ;; - -p|--pretty) - PRETTY=true - TAP=false - shift - ;; - -f|--filter) - FILTER="$2" - shift 2 - ;; - -c|--count) - COUNT_ONLY=true - shift - ;; - -d|--debug) - DEBUG=true - shift - ;; - -l|--list) - LIST_MODULES=true - shift - ;; - --dir) - TEST_DIRS+=("$2") - shift 2 - ;; - --all) - RUN_ALL=true - shift - ;; - -j|--jobs) - if [[ "$2" =~ ^[0-9]+$ ]]; then - ACORE_TEST_CORES="$2" - export ACORE_TEST_CORES - shift 2 - else - echo -e "${RED}Error: Invalid number of jobs specified: $2${NC}" - echo "Please provide a valid number." - exit 1 - fi - ;; - *.bats) - # Individual test files - TEST_FILES+=("$1") - shift - ;; - *) - # Assume it's a module name - TEST_MODULES+=("$1") - shift - ;; - esac -done - -# Check if BATS is installed -if ! command -v bats >/dev/null 2>&1; then - echo -e "${RED}Error: BATS is not installed${NC}" - echo "Please install BATS first:" - echo " sudo apt install bats # On Ubuntu/Debian" - echo " brew install bats-core # On macOS" - echo "Or run: make install-bats" - exit 1 -fi - -# Function to find test directories -find_test_directories() { - local search_paths=() - - if [[ "$RUN_ALL" == true ]]; then - # Find all test directories - mapfile -t search_paths < <(find "$PROJECT_ROOT/apps" -type d -name "test" 2>/dev/null) - elif [[ ${#TEST_DIRS[@]} -gt 0 ]]; then - # Use specified directories - for dir in "${TEST_DIRS[@]}"; do - if [[ -d "$PROJECT_ROOT/$dir/test" ]]; then - search_paths+=("$PROJECT_ROOT/$dir/test") - elif [[ -d "$dir/test" ]]; then - search_paths+=("$dir/test") - elif [[ -d "$dir" ]]; then - search_paths+=("$dir") - else - echo -e "${YELLOW}Warning: Test directory not found: $dir${NC}" - fi - done - elif [[ ${#TEST_MODULES[@]} -gt 0 ]]; then - # Use specified modules - for module in "${TEST_MODULES[@]}"; do - if [[ -d "$PROJECT_ROOT/apps/$module/test" ]]; then - search_paths+=("$PROJECT_ROOT/apps/$module/test") - else - echo -e "${YELLOW}Warning: Module test directory not found: $module${NC}" - fi - done - else - # Default: use current directory or startup-scripts if run from test-framework - if [[ "$(basename "$PWD")" == "test-framework" ]]; then - search_paths=("$PROJECT_ROOT/apps/startup-scripts/test") - elif [[ -d "./test" ]]; then - search_paths=("./test") - else - echo -e "${YELLOW}No test directory found. Use --all or specify a module.${NC}" - exit 0 - fi - fi - - echo "${search_paths[@]}" -} - -# Function to list available modules -list_modules() { - echo -e "${BLUE}Available test modules:${NC}" - find "$PROJECT_ROOT/apps" -type d -name "test" 2>/dev/null | while read -r test_dir; do - module_name=$(basename "$(dirname "$test_dir")") - test_count=$(find "$test_dir" -name "*.bats" | wc -l) - echo -e " ${GREEN}$module_name${NC} ($test_count test files)" - done -} - -# Show available modules if requested -if [[ "$LIST_MODULES" == true ]]; then - list_modules - exit 0 -fi - -# Find test directories -TEST_SEARCH_PATHS=($(find_test_directories)) - -if [[ ${#TEST_SEARCH_PATHS[@]} -eq 0 ]]; then - echo -e "${YELLOW}No test directories found.${NC}" - echo "Use --list to see available modules." - exit 0 -fi - -# Collect all test files -TEST_FILES=() -for test_dir in "${TEST_SEARCH_PATHS[@]}"; do - if [[ -d "$test_dir" ]]; then - if [[ -n "$FILTER" ]]; then - # Find test files matching filter - mapfile -t filtered_files < <(find "$test_dir" -name "*.bats" -exec grep -l "$FILTER" {} \; 2>/dev/null) - TEST_FILES+=("${filtered_files[@]}") - else - # Use all test files in directory - mapfile -t dir_files < <(find "$test_dir" -name "*.bats" 2>/dev/null) - TEST_FILES+=("${dir_files[@]}") - fi - fi -done - -if [[ ${#TEST_FILES[@]} -eq 0 ]]; then - if [[ -n "$FILTER" ]]; then - echo -e "${YELLOW}No test files found matching filter: $FILTER${NC}" - else - echo -e "${YELLOW}No test files found in specified directories.${NC}" - fi - exit 0 -fi - -# Show test count only -if [[ "$COUNT_ONLY" == true ]]; then - total_tests=0 - for file in "${TEST_FILES[@]}"; do - count=$(grep -c "^@test" "$file" 2>/dev/null || echo 0) - total_tests=$((total_tests + count)) - done - echo "Total tests: $total_tests" - echo "Test files: ${#TEST_FILES[@]}" - echo "Test directories: ${#TEST_SEARCH_PATHS[@]}" - exit 0 -fi - -# Build BATS command -BATS_CMD="bats --jobs $ACORE_TEST_CORES" - -# Set output format -if [[ "$TAP" == true ]]; then - BATS_CMD+=" --formatter tap" -elif [[ "$PRETTY" == true ]]; then - BATS_CMD+=" --formatter pretty" -fi - -# Enable verbose output -if [[ "$VERBOSE" == true ]]; then - BATS_CMD+=" --verbose-run" -fi - -# Add filter if specified -if [[ -n "$FILTER" ]]; then - BATS_CMD+=" --filter '$FILTER'" -fi - -# Add test files -BATS_CMD+=" ${TEST_FILES[*]}" - -echo -e "${BLUE}Running AzerothCore Tests with ${ACORE_TEST_CORES} jobs${NC}" -echo -e "${YELLOW}Test directories: ${TEST_SEARCH_PATHS[*]}${NC}" -echo -e "${YELLOW}Test files: ${#TEST_FILES[@]}${NC}" -if [[ -n "$FILTER" ]]; then - echo -e "${YELLOW}Filter: $FILTER${NC}" -fi -echo "" - -# Run tests -if [[ "$DEBUG" == true ]]; then - echo -e "${YELLOW}Command: $BATS_CMD${NC}" - echo "" -fi - -# Execute BATS -if eval "$BATS_CMD"; then - echo "" - echo -e "${GREEN}✅ All tests passed!${NC}" - exit 0 -else - exit_code=$? - echo "" - echo -e "${RED}❌ Some tests failed!${NC}" - - if [[ "$DEBUG" == true ]]; then - echo -e "${YELLOW}Tip: Check the output above for detailed error information${NC}" - echo -e "${YELLOW}You can also run individual tests for more detailed debugging:${NC}" - echo -e "${YELLOW} $0 --verbose --filter ${NC}" - fi - - exit $exit_code -fi diff --git a/apps/test-framework/test-main.sh b/apps/test-framework/test-main.sh new file mode 100644 index 0000000000..c3a122989d --- /dev/null +++ b/apps/test-framework/test-main.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + + # shellcheck source-path=SCRIPTDIR +CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# shellcheck source=../bash_shared/includes.sh +source "$CURRENT_PATH/../bash_shared/includes.sh" +# shellcheck source=../bash_shared/menu_system.sh +source "$AC_PATH_APPS/bash_shared/menu_system.sh" + +# Menu: single ordered source of truth (no functions in strings) +# Format: "key|short|description" +menu_items=( + "bash|b|Run Bash tests" + "core|c|Run AzerothCore tests" + "quit|q|Exit from this menu" +) + + +# Menu command handler - called by menu system for each command +function handle_menu_command() { + local key="$1" + shift + + case "$key" in + "bash") + bash "$CURRENT_PATH/run-bash-tests.sh" "${@:-"--all"}" + ;; + "core") + # shellcheck source=./run-core-tests.sh + bash "$CURRENT_PATH/run-core-tests.sh" "$@" + ;; + "quit") + echo "Goodbye!" + exit + ;; + *) + echo "Invalid option. Use --help to see available commands." + return 1 + ;; + esac +} + +# Run the menu system +menu_run_with_items "TEST FRAMEWORK" handle_menu_command -- "${menu_items[@]}" -- "$@" diff --git a/conf/dist/config.sh b/conf/dist/config.sh index 1f956ca70e..e33d19f212 100644 --- a/conf/dist/config.sh +++ b/conf/dist/config.sh @@ -108,6 +108,15 @@ CCUSTOMOPTIONS=${CCUSTOMOPTIONS:-''} AC_CCACHE=${AC_CCACHE:-false} export CCACHE_DIR=${CCACHE_DIR:-"$AC_PATH_VAR/ccache"} +# +# Enable running the cmake install as root +# Installing as root allows to set the SUID bit on +# the worldserver binary. This is required if you want +# to bind the worldserver to reserved ports +# Default: 0 (false) +# +export AC_ENABLE_ROOT_CMAKE_INSTALL=${AC_ENABLE_ROOT_CMAKE_INSTALL:-0} + ############################################## # # GOOGLE PERF TOOLS diff --git a/src/test/mocks/WorldMock.h b/src/test/mocks/WorldMock.h index 75e8438e4e..db9ca51942 100644 --- a/src/test/mocks/WorldMock.h +++ b/src/test/mocks/WorldMock.h @@ -26,7 +26,7 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" -void AddScripts() {} +inline void AddScripts() {} class WorldMock: public IWorld { diff --git a/src/test/server/game/Battlegrounds/ArenaSeason/ArenaSeasonRewardDistributorTest.cpp b/src/test/server/game/Battlegrounds/ArenaSeason/ArenaSeasonRewardDistributorTest.cpp index 300cfc4e07..82f68e8634 100644 --- a/src/test/server/game/Battlegrounds/ArenaSeason/ArenaSeasonRewardDistributorTest.cpp +++ b/src/test/server/game/Battlegrounds/ArenaSeason/ArenaSeasonRewardDistributorTest.cpp @@ -19,6 +19,8 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "ArenaSeasonRewardsDistributor.h" +#include "WorldMock.h" +#include class MockArenaSeasonTeamRewarder : public ArenaSeasonTeamRewarder { @@ -31,12 +33,26 @@ class ArenaSeasonRewardDistributorTest : public ::testing::Test protected: void SetUp() override { + _previousWorld = std::move(sWorld); + _worldMock = new ::testing::NiceMock(); + ON_CALL(*_worldMock, getIntConfig(::testing::_)).WillByDefault(::testing::Return(0)); + ON_CALL(*_worldMock, getIntConfig(CONFIG_LEGACY_ARENA_START_RATING)).WillByDefault(::testing::Return(1500)); + ON_CALL(*_worldMock, getIntConfig(CONFIG_ARENA_START_RATING)).WillByDefault(::testing::Return(0)); + sWorld.reset(_worldMock); + _mockRewarder = std::make_unique(); _distributor = std::make_unique(_mockRewarder.get()); } + void TearDown() override + { + sWorld = std::move(_previousWorld); + } + std::unique_ptr _mockRewarder; std::unique_ptr _distributor; + std::unique_ptr _previousWorld; + ::testing::NiceMock* _worldMock = nullptr; }; ArenaTeam ArenaTeamWithRating(int rating, int gamesPlayed) diff --git a/src/test/server/game/Battlegrounds/ArenaSeason/ArenaTeamFilterTest.cpp b/src/test/server/game/Battlegrounds/ArenaSeason/ArenaTeamFilterTest.cpp index 9db54f2000..7863f56002 100644 --- a/src/test/server/game/Battlegrounds/ArenaSeason/ArenaTeamFilterTest.cpp +++ b/src/test/server/game/Battlegrounds/ArenaSeason/ArenaTeamFilterTest.cpp @@ -22,6 +22,7 @@ #include "ArenaTeamMgr.h" #include "ArenaTeam.h" #include +#include "WorldMock.h" // Used to expose Type property. class ArenaTeamTest : public ArenaTeam @@ -46,6 +47,13 @@ class ArenaTeamFilterTest : public ::testing::Test protected: void SetUp() override { + _previousWorld = std::move(sWorld); + _worldMock = new ::testing::NiceMock(); + ON_CALL(*_worldMock, getIntConfig(::testing::_)).WillByDefault(::testing::Return(0)); + ON_CALL(*_worldMock, getIntConfig(CONFIG_LEGACY_ARENA_START_RATING)).WillByDefault(::testing::Return(1500)); + ON_CALL(*_worldMock, getIntConfig(CONFIG_ARENA_START_RATING)).WillByDefault(::testing::Return(0)); + sWorld.reset(_worldMock); + team1 = ArenaTeamWithType(2); // 2v2 team2 = ArenaTeamWithType(3); // 3v3 team3 = ArenaTeamWithType(5); // 5v5 @@ -60,12 +68,16 @@ protected: delete team1; delete team2; delete team3; + + sWorld = std::move(_previousWorld); } ArenaTeamMgr::ArenaTeamContainer arenaTeams; ArenaTeam* team1; ArenaTeam* team2; ArenaTeam* team3; + std::unique_ptr _previousWorld; + ::testing::NiceMock* _worldMock = nullptr; }; // Test for ArenaTeamFilterAllTeams: it should return all teams without filtering -- cgit v1.2.3