mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-24 22:26:22 +00:00
Merge branch 'master' into Playerbot
This commit is contained in:
@@ -28,7 +28,8 @@ results = {
|
||||
"INSERT & DELETE safety usage check": "Passed",
|
||||
"Missing semicolon check": "Passed",
|
||||
"Backtick check": "Passed",
|
||||
"Directory check": "Passed"
|
||||
"Directory check": "Passed",
|
||||
"Table engine check": "Passed"
|
||||
}
|
||||
|
||||
# Collect all files in all directories
|
||||
@@ -78,6 +79,7 @@ def parsing_file(files: list) -> None:
|
||||
insert_delete_safety_check(file, file_path)
|
||||
semicolon_check(file, file_path)
|
||||
backtick_check(file, file_path)
|
||||
non_innodb_engine_check(file, file_path)
|
||||
except UnicodeDecodeError:
|
||||
print(f"\n❌ Could not decode file {file_path}")
|
||||
sys.exit(1)
|
||||
@@ -383,6 +385,25 @@ def directory_check(file: io, file_path: str) -> None:
|
||||
error_handler = True
|
||||
results["Directory check"] = "Failed"
|
||||
|
||||
def non_innodb_engine_check(file: io, file_path: str) -> None:
|
||||
global error_handler, results
|
||||
file.seek(0)
|
||||
check_failed = False
|
||||
|
||||
engine_pattern = re.compile(r'ENGINE\s*=\s*([a-zA-Z0-9_]+)', re.IGNORECASE)
|
||||
|
||||
for line_number, line in enumerate(file, start=1):
|
||||
match = engine_pattern.search(line)
|
||||
if match:
|
||||
engine = match.group(1).lower()
|
||||
if engine != "innodb":
|
||||
print(f"❌ Non-InnoDB engine found: '{engine}' in {file_path} at line {line_number}")
|
||||
check_failed = True
|
||||
|
||||
if check_failed:
|
||||
error_handler = True
|
||||
results["Table engine check"] = "Failed"
|
||||
|
||||
# Collect all files from matching directories
|
||||
all_files = collect_files_from_directories(src_directory) + collect_files_from_directories(base_directory) + collect_files_from_directories(archive_directory)
|
||||
|
||||
|
||||
17
apps/compiler/test/bats.conf
Normal file
17
apps/compiler/test/bats.conf
Normal file
@@ -0,0 +1,17 @@
|
||||
# BATS Test Configuration for Compiler App
|
||||
|
||||
# Set test timeout (in seconds)
|
||||
export BATS_TEST_TIMEOUT=60
|
||||
|
||||
# Enable verbose output for debugging
|
||||
export BATS_VERBOSE_RUN=1
|
||||
|
||||
# Test output format
|
||||
export BATS_FORMATTER=pretty
|
||||
|
||||
# Enable colored output
|
||||
export BATS_NO_PARALLELIZE_ACROSS_FILES=1
|
||||
export BATS_NO_PARALLELIZE_WITHIN_FILE=1
|
||||
|
||||
# Compiler specific test configuration
|
||||
export COMPILER_TEST_SKIP_HEAVY=1
|
||||
307
apps/compiler/test/test_compiler.bats
Executable file
307
apps/compiler/test/test_compiler.bats
Executable file
@@ -0,0 +1,307 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
# Require minimum BATS version to avoid warnings
|
||||
bats_require_minimum_version 1.5.0
|
||||
|
||||
# AzerothCore Compiler Scripts Test Suite
|
||||
# Tests the functionality of the compiler scripts using the unified test framework
|
||||
|
||||
# Load the AzerothCore test framework
|
||||
load '../../test-framework/bats_libs/acore-support'
|
||||
load '../../test-framework/bats_libs/acore-assert'
|
||||
|
||||
# Setup that runs before each test
|
||||
setup() {
|
||||
compiler_setup
|
||||
export SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
||||
export COMPILER_SCRIPT="$SCRIPT_DIR/compiler.sh"
|
||||
}
|
||||
|
||||
# Cleanup that runs after each test
|
||||
teardown() {
|
||||
acore_test_teardown
|
||||
}
|
||||
|
||||
# ===== COMPILER SCRIPT TESTS =====
|
||||
|
||||
@test "compiler: should show help with --help argument" {
|
||||
run bash -c "echo '' | timeout 5s $COMPILER_SCRIPT --help"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Available commands:" ]]
|
||||
}
|
||||
|
||||
@test "compiler: should show help with empty input" {
|
||||
run bash -c "echo '' | timeout 5s $COMPILER_SCRIPT 2>&1 || true"
|
||||
# The script might exit with timeout (124) or success (0), both are acceptable for this test
|
||||
[[ "$status" -eq 0 ]] || [[ "$status" -eq 124 ]]
|
||||
# Check if output contains expected content - looking for menu options
|
||||
[[ "$output" =~ "build:" ]] || [[ "$output" =~ "clean:" ]] || [[ "$output" =~ "Please enter your choice" ]] || [[ -z "$output" ]]
|
||||
}
|
||||
|
||||
@test "compiler: should accept option numbers" {
|
||||
# Test option 7 (ccacheShowStats) which should be safe to run
|
||||
run bash -c "echo '7' | timeout 10s $COMPILER_SCRIPT 2>/dev/null || true"
|
||||
# The script might exit with timeout (124) or success (0), both are acceptable
|
||||
[[ "$status" -eq 0 ]] || [[ "$status" -eq 124 ]]
|
||||
}
|
||||
|
||||
@test "compiler: should accept option by name" {
|
||||
run timeout 10s "$COMPILER_SCRIPT" ccacheShowStats
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "compiler: should handle invalid option gracefully" {
|
||||
run timeout 5s "$COMPILER_SCRIPT" invalidOption
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "invalid option" ]]
|
||||
}
|
||||
|
||||
@test "compiler: should handle invalid number gracefully" {
|
||||
run bash -c "echo '999' | timeout 5s $COMPILER_SCRIPT 2>/dev/null || true"
|
||||
# The script might exit with timeout (124) or success (0), both are acceptable
|
||||
[[ "$status" -eq 0 ]] || [[ "$status" -eq 124 ]]
|
||||
# Check if output contains expected content, or if there's no output due to timeout, that's also acceptable
|
||||
[[ "$output" =~ "invalid option" ]] || [[ "$output" =~ "Please enter your choice" ]] || [[ -z "$output" ]]
|
||||
}
|
||||
|
||||
@test "compiler: should quit with quit option" {
|
||||
run timeout 5s "$COMPILER_SCRIPT" quit
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
# ===== FUNCTION TESTS =====
|
||||
|
||||
@test "functions: comp_clean should handle non-existent build directory" {
|
||||
# Source the functions with a non-existent build path
|
||||
run bash -c "
|
||||
export BUILDPATH='/tmp/non_existent_build_dir_$RANDOM'
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
comp_clean
|
||||
"
|
||||
# Accept either success or failure - the important thing is the function runs
|
||||
[[ "$status" -eq 0 ]] || [[ "$status" -eq 1 ]]
|
||||
[[ "$output" =~ "Cleaning build files" ]]
|
||||
}
|
||||
|
||||
@test "functions: comp_clean should remove build files when directory exists" {
|
||||
# Create a temporary build directory with test files
|
||||
local test_build_dir="/tmp/test_build_$RANDOM"
|
||||
mkdir -p "$test_build_dir/subdir"
|
||||
touch "$test_build_dir/test_file.txt"
|
||||
touch "$test_build_dir/subdir/nested_file.txt"
|
||||
|
||||
# Run the clean function
|
||||
run bash -c "
|
||||
export BUILDPATH='$test_build_dir'
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
comp_clean
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Cleaning build files" ]]
|
||||
# Directory should still exist but be empty
|
||||
[ -d "$test_build_dir" ]
|
||||
[ ! -f "$test_build_dir/test_file.txt" ]
|
||||
[ ! -f "$test_build_dir/subdir/nested_file.txt" ]
|
||||
|
||||
# Cleanup
|
||||
rm -rf "$test_build_dir"
|
||||
}
|
||||
|
||||
@test "functions: comp_ccacheShowStats should run without errors when ccache enabled" {
|
||||
run bash -c "
|
||||
export AC_CCACHE=true
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
comp_ccacheShowStats
|
||||
"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "functions: comp_ccacheShowStats should do nothing when ccache disabled" {
|
||||
run bash -c "
|
||||
export AC_CCACHE=false
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
comp_ccacheShowStats
|
||||
"
|
||||
[ "$status" -eq 0 ]
|
||||
# Should produce no output when ccache is disabled
|
||||
}
|
||||
|
||||
@test "functions: comp_ccacheClean should handle disabled ccache" {
|
||||
run bash -c "
|
||||
export AC_CCACHE=false
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
comp_ccacheClean
|
||||
"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "ccache is disabled" ]]
|
||||
}
|
||||
|
||||
@test "functions: comp_ccacheClean should run when ccache enabled" {
|
||||
# Only run if ccache is actually available
|
||||
if command -v ccache >/dev/null 2>&1; then
|
||||
run bash -c "
|
||||
export AC_CCACHE=true
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
comp_ccacheClean
|
||||
"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Cleaning ccache" ]]
|
||||
else
|
||||
skip "ccache not available on system"
|
||||
fi
|
||||
}
|
||||
|
||||
@test "functions: comp_ccacheEnable should set environment variables" {
|
||||
# Call the function in a subshell to capture environment changes
|
||||
run bash -c "
|
||||
export AC_CCACHE=true
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
comp_ccacheEnable
|
||||
env | grep CCACHE | head -5
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "CCACHE_MAXSIZE" ]] || [[ "$output" =~ "CCACHE_COMPRESS" ]]
|
||||
}
|
||||
|
||||
@test "functions: comp_ccacheEnable should not set variables when ccache disabled" {
|
||||
# Call the function and verify it returns early when ccache is disabled
|
||||
run bash -c "
|
||||
export AC_CCACHE=false
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
comp_ccacheEnable
|
||||
# The function should return early, so we check if it completed successfully
|
||||
echo 'Function completed without setting CCACHE vars'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Function completed" ]]
|
||||
}
|
||||
|
||||
# Mock tests for build functions (these would normally require a full setup)
|
||||
@test "functions: comp_configure should detect platform" {
|
||||
# Mock cmake command to avoid actual configuration
|
||||
run -127 bash -c "
|
||||
function cmake() {
|
||||
echo 'CMAKE called with args: $*'
|
||||
return 0
|
||||
}
|
||||
export -f cmake
|
||||
|
||||
# Set required variables
|
||||
export BUILDPATH='/tmp'
|
||||
export SRCPATH='/tmp'
|
||||
export BINPATH='/tmp'
|
||||
export CTYPE='Release'
|
||||
|
||||
# Source the functions
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
|
||||
# Run configure in the /tmp directory
|
||||
cd /tmp && comp_configure
|
||||
"
|
||||
|
||||
# Accept command not found as this might indicate missing dependencies
|
||||
[[ "$status" -eq 0 ]] || [[ "$status" -eq 127 ]]
|
||||
# If successful, check for expected output
|
||||
if [ "$status" -eq 0 ]; then
|
||||
[[ "$output" =~ "Platform:" ]] || [[ "$output" =~ "CMAKE called with args:" ]]
|
||||
fi
|
||||
}
|
||||
|
||||
@test "functions: comp_compile should detect thread count" {
|
||||
# Mock cmake command to avoid actual compilation
|
||||
run -127 bash -c "
|
||||
function cmake() {
|
||||
echo 'CMAKE called with args: $*'
|
||||
return 0
|
||||
}
|
||||
export -f cmake
|
||||
|
||||
# Mock other commands
|
||||
function pushd() { echo 'pushd $*'; }
|
||||
function popd() { echo 'popd $*'; }
|
||||
function time() { shift; \"\$@\"; }
|
||||
export -f pushd popd time
|
||||
|
||||
# Set required variables
|
||||
export BUILDPATH='/tmp'
|
||||
export MTHREADS=0
|
||||
export CTYPE='Release'
|
||||
export AC_BINPATH_FULL='/tmp'
|
||||
|
||||
# Source the functions
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
|
||||
# Run compile in the /tmp directory
|
||||
cd /tmp && comp_compile
|
||||
"
|
||||
|
||||
# Accept command not found as this might indicate missing dependencies
|
||||
[[ "$status" -eq 0 ]] || [[ "$status" -eq 127 ]]
|
||||
# If successful, check for expected output
|
||||
if [ "$status" -eq 0 ]; then
|
||||
[[ "$output" =~ "pushd" ]] || [[ "$output" =~ "CMAKE called with args:" ]]
|
||||
fi
|
||||
}
|
||||
|
||||
@test "functions: comp_build should call configure and compile" {
|
||||
# Mock the comp_configure and comp_compile functions
|
||||
run -127 bash -c "
|
||||
function comp_configure() {
|
||||
echo 'comp_configure called'
|
||||
return 0
|
||||
}
|
||||
|
||||
function comp_compile() {
|
||||
echo 'comp_compile called'
|
||||
return 0
|
||||
}
|
||||
|
||||
export -f comp_configure comp_compile
|
||||
|
||||
# Source the functions
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
|
||||
# Run build
|
||||
comp_build
|
||||
"
|
||||
|
||||
# Accept command not found as this might indicate missing dependencies
|
||||
[[ "$status" -eq 0 ]] || [[ "$status" -eq 127 ]]
|
||||
# If successful, check for expected output
|
||||
if [ "$status" -eq 0 ]; then
|
||||
[[ "$output" =~ "comp_configure called" ]] && [[ "$output" =~ "comp_compile called" ]]
|
||||
fi
|
||||
}
|
||||
|
||||
@test "functions: comp_all should call clean and build" {
|
||||
# Mock the comp_clean and comp_build functions
|
||||
run -127 bash -c "
|
||||
function comp_clean() {
|
||||
echo 'comp_clean called'
|
||||
return 0
|
||||
}
|
||||
|
||||
function comp_build() {
|
||||
echo 'comp_build called'
|
||||
return 0
|
||||
}
|
||||
|
||||
export -f comp_clean comp_build
|
||||
|
||||
# Source the functions
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
|
||||
# Run all
|
||||
comp_all
|
||||
"
|
||||
|
||||
# Accept command not found as this might indicate missing dependencies
|
||||
[[ "$status" -eq 0 ]] || [[ "$status" -eq 127 ]]
|
||||
# If successful, check for expected output
|
||||
if [ "$status" -eq 0 ]; then
|
||||
[[ "$output" =~ "comp_clean called" ]] && [[ "$output" =~ "comp_build called" ]]
|
||||
fi
|
||||
}
|
||||
211
apps/compiler/test/test_compiler_config.bats
Executable file
211
apps/compiler/test/test_compiler_config.bats
Executable file
@@ -0,0 +1,211 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
# AzerothCore Compiler Configuration Test Suite
|
||||
# Tests the configuration and support scripts for the compiler module
|
||||
|
||||
# Load the AzerothCore test framework
|
||||
load '../../test-framework/bats_libs/acore-support'
|
||||
load '../../test-framework/bats_libs/acore-assert'
|
||||
|
||||
# Setup that runs before each test
|
||||
setup() {
|
||||
compiler_setup
|
||||
export SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
||||
}
|
||||
|
||||
# Cleanup that runs after each test
|
||||
teardown() {
|
||||
acore_test_teardown
|
||||
}
|
||||
|
||||
# ===== DEFINES SCRIPT TESTS =====
|
||||
|
||||
@test "defines: should accept CCTYPE from argument" {
|
||||
# Test the defines script with a release argument
|
||||
run bash -c "unset CCTYPE; source '$SCRIPT_DIR/includes/defines.sh' release; echo \"CCTYPE=\$CCTYPE\""
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "CCTYPE=Release" ]]
|
||||
}
|
||||
|
||||
@test "defines: should handle uppercase CCTYPE" {
|
||||
# Test the defines script with an uppercase argument
|
||||
run bash -c "unset CCTYPE; source '$SCRIPT_DIR/includes/defines.sh' DEBUG; echo \"CCTYPE=\$CCTYPE\""
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "CCTYPE=DEBUG" ]]
|
||||
}
|
||||
|
||||
@test "defines: should handle lowercase input" {
|
||||
# Test the defines script with lowercase input
|
||||
run bash -c "unset CCTYPE; source '$SCRIPT_DIR/includes/defines.sh' debug; echo \"CCTYPE=\$CCTYPE\""
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "CCTYPE=Debug" ]]
|
||||
}
|
||||
|
||||
@test "defines: should handle mixed case input" {
|
||||
# Test the defines script with mixed case input
|
||||
run bash -c "unset CCTYPE; source '$SCRIPT_DIR/includes/defines.sh' rElEaSe; echo \"CCTYPE=\$CCTYPE\""
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "CCTYPE=RElEaSe" ]]
|
||||
}
|
||||
|
||||
@test "defines: should handle no argument" {
|
||||
# Test the defines script with no argument
|
||||
run bash -c "CCTYPE='original'; source '$SCRIPT_DIR/includes/defines.sh'; echo \"CCTYPE=\$CCTYPE\""
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "CCTYPE=original" ]]
|
||||
}
|
||||
|
||||
# ===== INCLUDES SCRIPT TESTS =====
|
||||
|
||||
@test "includes: should create necessary directories" {
|
||||
# Create a temporary test environment
|
||||
local temp_dir="/tmp/compiler_test_$RANDOM"
|
||||
local build_path="$temp_dir/build"
|
||||
local bin_path="$temp_dir/bin"
|
||||
|
||||
# Remove directories to test creation
|
||||
rm -rf "$temp_dir"
|
||||
|
||||
# Source the includes script with custom paths - use a simpler approach
|
||||
run bash -c "
|
||||
export BUILDPATH='$build_path'
|
||||
export BINPATH='$bin_path'
|
||||
export AC_PATH_APPS='$SCRIPT_DIR/..'
|
||||
|
||||
# Create directories manually since includes.sh does this
|
||||
mkdir -p \"\$BUILDPATH\"
|
||||
mkdir -p \"\$BINPATH\"
|
||||
|
||||
echo 'Directories created'
|
||||
[ -d '$build_path' ] && echo 'BUILD_EXISTS'
|
||||
[ -d '$bin_path' ] && echo 'BIN_EXISTS'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "BUILD_EXISTS" ]]
|
||||
[[ "$output" =~ "BIN_EXISTS" ]]
|
||||
|
||||
# Cleanup
|
||||
rm -rf "$temp_dir"
|
||||
}
|
||||
|
||||
@test "includes: should source required files" {
|
||||
# Test that all required files are sourced without errors
|
||||
run bash -c "
|
||||
# Set minimal required environment
|
||||
AC_PATH_APPS='$SCRIPT_DIR/..'
|
||||
BUILDPATH='/tmp'
|
||||
BINPATH='/tmp'
|
||||
source '$SCRIPT_DIR/includes/includes.sh'
|
||||
echo 'All files sourced successfully'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "All files sourced successfully" ]]
|
||||
}
|
||||
|
||||
@test "includes: should set AC_PATH_COMPILER variable" {
|
||||
# Test that AC_PATH_COMPILER is set correctly
|
||||
run bash -c "
|
||||
AC_PATH_APPS='$SCRIPT_DIR/..'
|
||||
BUILDPATH='/tmp'
|
||||
BINPATH='/tmp'
|
||||
source '$SCRIPT_DIR/includes/includes.sh'
|
||||
echo \"AC_PATH_COMPILER=\$AC_PATH_COMPILER\"
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "AC_PATH_COMPILER=" ]]
|
||||
[[ "$output" =~ "/compiler" ]]
|
||||
}
|
||||
|
||||
@test "includes: should register ON_AFTER_BUILD hook" {
|
||||
# Test that the hook is registered
|
||||
run bash -c "
|
||||
AC_PATH_APPS='$SCRIPT_DIR/..'
|
||||
BUILDPATH='/tmp'
|
||||
BINPATH='/tmp'
|
||||
source '$SCRIPT_DIR/includes/includes.sh'
|
||||
# Check if the function exists
|
||||
type ac_on_after_build > /dev/null && echo 'HOOK_FUNCTION_EXISTS'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "HOOK_FUNCTION_EXISTS" ]]
|
||||
}
|
||||
|
||||
# ===== CONFIGURATION TESTS =====
|
||||
|
||||
@test "config: should handle missing config file gracefully" {
|
||||
# Test behavior when config.sh doesn't exist
|
||||
run bash -c "
|
||||
export AC_PATH_APPS='$SCRIPT_DIR/..'
|
||||
export AC_PATH_COMPILER='$SCRIPT_DIR'
|
||||
export BUILDPATH='/tmp'
|
||||
export BINPATH='/tmp'
|
||||
|
||||
# Test that missing config doesn't break sourcing
|
||||
[ ! -f '$SCRIPT_DIR/config.sh' ] && echo 'NO_CONFIG_FILE'
|
||||
echo 'Config handled successfully'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Config handled successfully" ]]
|
||||
}
|
||||
|
||||
# ===== ENVIRONMENT VARIABLE TESTS =====
|
||||
|
||||
@test "environment: should handle platform detection" {
|
||||
# Test that OSTYPE is properly handled
|
||||
run bash -c "
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
echo \"Platform detected: \$OSTYPE\"
|
||||
case \"\$OSTYPE\" in
|
||||
linux*) echo 'LINUX_DETECTED' ;;
|
||||
darwin*) echo 'DARWIN_DETECTED' ;;
|
||||
msys*) echo 'MSYS_DETECTED' ;;
|
||||
*) echo 'UNKNOWN_PLATFORM' ;;
|
||||
esac
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Platform detected:" ]]
|
||||
# Should detect at least one known platform
|
||||
[[ "$output" =~ "LINUX_DETECTED" ]] || [[ "$output" =~ "DARWIN_DETECTED" ]] || [[ "$output" =~ "MSYS_DETECTED" ]] || [[ "$output" =~ "UNKNOWN_PLATFORM" ]]
|
||||
}
|
||||
|
||||
@test "environment: should handle missing environment variables gracefully" {
|
||||
# Test behavior with minimal environment
|
||||
run bash -c "
|
||||
unset BUILDPATH BINPATH SRCPATH MTHREADS
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
echo 'Functions loaded with minimal environment'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Functions loaded with minimal environment" ]]
|
||||
}
|
||||
|
||||
# ===== HOOK SYSTEM TESTS =====
|
||||
|
||||
@test "hooks: ac_on_after_build should copy startup scripts" {
|
||||
# Mock the cp command to test the hook
|
||||
function cp() {
|
||||
echo "CP called with args: $*"
|
||||
return 0
|
||||
}
|
||||
export -f cp
|
||||
|
||||
# Set required variables
|
||||
AC_PATH_APPS="$SCRIPT_DIR/.."
|
||||
BINPATH="/tmp/test_bin"
|
||||
export AC_PATH_APPS BINPATH
|
||||
|
||||
# Source and test the hook function
|
||||
source "$SCRIPT_DIR/includes/includes.sh"
|
||||
run ac_on_after_build
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "CP called with args:" ]]
|
||||
[[ "$output" =~ "startup-scripts" ]]
|
||||
}
|
||||
254
apps/compiler/test/test_compiler_integration.bats
Executable file
254
apps/compiler/test/test_compiler_integration.bats
Executable file
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
# AzerothCore Compiler Integration Test Suite
|
||||
# Tests edge cases and integration scenarios for the compiler module
|
||||
|
||||
# Load the AzerothCore test framework
|
||||
load '../../test-framework/bats_libs/acore-support'
|
||||
load '../../test-framework/bats_libs/acore-assert'
|
||||
|
||||
# Setup that runs before each test
|
||||
setup() {
|
||||
compiler_setup
|
||||
export SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)"
|
||||
}
|
||||
|
||||
# Cleanup that runs after each test
|
||||
teardown() {
|
||||
acore_test_teardown
|
||||
}
|
||||
|
||||
# ===== INTEGRATION TESTS =====
|
||||
|
||||
@test "integration: should handle full compiler.sh workflow" {
|
||||
# Test the complete workflow with safe options
|
||||
run bash -c "
|
||||
cd '$SCRIPT_DIR'
|
||||
echo '7' | timeout 15s ./compiler.sh
|
||||
echo 'First command completed'
|
||||
echo 'quit' | timeout 10s ./compiler.sh
|
||||
echo 'Quit command completed'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "First command completed" ]]
|
||||
[[ "$output" =~ "Quit command completed" ]]
|
||||
}
|
||||
|
||||
@test "integration: should handle multiple consecutive commands" {
|
||||
# Test running multiple safe commands in sequence
|
||||
run bash -c "
|
||||
cd '$SCRIPT_DIR'
|
||||
timeout 10s ./compiler.sh ccacheShowStats
|
||||
echo 'Command 1 done'
|
||||
timeout 10s ./compiler.sh quit
|
||||
echo 'Command 2 done'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Command 1 done" ]]
|
||||
[[ "$output" =~ "Command 2 done" ]]
|
||||
}
|
||||
|
||||
@test "integration: should preserve working directory" {
|
||||
# Test that the script doesn't change the working directory unexpectedly
|
||||
local original_pwd="$(pwd)"
|
||||
|
||||
run bash -c "
|
||||
cd '$SCRIPT_DIR'
|
||||
original_dir=\$(pwd)
|
||||
timeout 10s ./compiler.sh quit
|
||||
current_dir=\$(pwd)
|
||||
echo \"ORIGINAL: \$original_dir\"
|
||||
echo \"CURRENT: \$current_dir\"
|
||||
[ \"\$original_dir\" = \"\$current_dir\" ] && echo 'DIRECTORY_PRESERVED'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "DIRECTORY_PRESERVED" ]]
|
||||
}
|
||||
|
||||
# ===== ERROR HANDLING TESTS =====
|
||||
|
||||
@test "error_handling: should handle script errors gracefully" {
|
||||
# Test script behavior with set -e when encountering errors
|
||||
run bash -c "
|
||||
cd '$SCRIPT_DIR'
|
||||
# Try to source a non-existent file to test error handling
|
||||
timeout 5s bash -c 'set -e; source /nonexistent/file.sh' || echo 'ERROR_HANDLED'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "ERROR_HANDLED" ]]
|
||||
}
|
||||
|
||||
@test "error_handling: should validate function availability" {
|
||||
# Test that required functions are available after sourcing
|
||||
run bash -c "
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
|
||||
# Check for key functions
|
||||
type comp_clean > /dev/null && echo 'COMP_CLEAN_AVAILABLE'
|
||||
type comp_configure > /dev/null && echo 'COMP_CONFIGURE_AVAILABLE'
|
||||
type comp_compile > /dev/null && echo 'COMP_COMPILE_AVAILABLE'
|
||||
type comp_build > /dev/null && echo 'COMP_BUILD_AVAILABLE'
|
||||
type comp_all > /dev/null && echo 'COMP_ALL_AVAILABLE'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "COMP_CLEAN_AVAILABLE" ]]
|
||||
[[ "$output" =~ "COMP_CONFIGURE_AVAILABLE" ]]
|
||||
[[ "$output" =~ "COMP_COMPILE_AVAILABLE" ]]
|
||||
[[ "$output" =~ "COMP_BUILD_AVAILABLE" ]]
|
||||
[[ "$output" =~ "COMP_ALL_AVAILABLE" ]]
|
||||
}
|
||||
|
||||
# ===== PERMISSION TESTS =====
|
||||
|
||||
@test "permissions: should handle permission requirements" {
|
||||
# Test script behavior with different permission scenarios
|
||||
run bash -c "
|
||||
# Test SUDO variable detection
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
echo \"SUDO variable: '\$SUDO'\"
|
||||
[ -n \"\$SUDO\" ] && echo 'SUDO_SET' || echo 'SUDO_EMPTY'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
# Should set SUDO appropriately based on EUID
|
||||
[[ "$output" =~ "SUDO_SET" ]] || [[ "$output" =~ "SUDO_EMPTY" ]]
|
||||
}
|
||||
|
||||
# ===== CLEANUP TESTS =====
|
||||
|
||||
@test "cleanup: comp_clean should handle various file types" {
|
||||
# Create a comprehensive test directory structure
|
||||
local test_dir="/tmp/compiler_cleanup_test_$RANDOM"
|
||||
mkdir -p "$test_dir/subdir1/subdir2"
|
||||
|
||||
# Create various file types
|
||||
touch "$test_dir/regular_file.txt"
|
||||
touch "$test_dir/executable_file.sh"
|
||||
touch "$test_dir/.hidden_file"
|
||||
touch "$test_dir/subdir1/nested_file.obj"
|
||||
touch "$test_dir/subdir1/subdir2/deep_file.a"
|
||||
ln -s "$test_dir/regular_file.txt" "$test_dir/symlink_file"
|
||||
|
||||
# Make one file executable
|
||||
chmod +x "$test_dir/executable_file.sh"
|
||||
|
||||
# Test cleanup
|
||||
run bash -c "
|
||||
export BUILDPATH='$test_dir'
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
comp_clean
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Cleaning build files" ]]
|
||||
|
||||
# Verify cleanup (directory should exist but files should be cleaned)
|
||||
[ -d "$test_dir" ]
|
||||
|
||||
# The cleanup might not remove all files depending on the implementation
|
||||
# Let's check if at least some cleanup occurred
|
||||
local remaining_files=$(find "$test_dir" -type f | wc -l)
|
||||
# Either all files are gone, or at least some cleanup happened
|
||||
[[ "$remaining_files" -eq 0 ]] || [[ "$remaining_files" -lt 6 ]]
|
||||
|
||||
# Cleanup test directory
|
||||
rm -rf "$test_dir"
|
||||
}
|
||||
|
||||
# ===== THREAD DETECTION TESTS =====
|
||||
|
||||
@test "threading: should detect available CPU cores" {
|
||||
# Test thread count detection logic
|
||||
run bash -c "
|
||||
# Simulate the thread detection logic from the actual function
|
||||
MTHREADS=0
|
||||
if [ \$MTHREADS == 0 ]; then
|
||||
# Use nproc if available, otherwise simulate 4 cores
|
||||
if command -v nproc >/dev/null 2>&1; then
|
||||
MTHREADS=\$(nproc)
|
||||
else
|
||||
MTHREADS=4
|
||||
fi
|
||||
MTHREADS=\$((MTHREADS + 2))
|
||||
fi
|
||||
echo \"Detected threads: \$MTHREADS\"
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "Detected threads:" ]]
|
||||
# Should be at least 3 (1 core + 2)
|
||||
local thread_count=$(echo "$output" | grep -o '[0-9]\+')
|
||||
[ "$thread_count" -ge 3 ]
|
||||
}
|
||||
|
||||
# ===== CMAKE OPTION TESTS =====
|
||||
|
||||
@test "cmake: should build correct cmake command" {
|
||||
# Mock cmake to capture command line arguments
|
||||
run bash -c "
|
||||
function cmake() {
|
||||
echo 'CMAKE_COMMAND: $*'
|
||||
return 0
|
||||
}
|
||||
export -f cmake
|
||||
|
||||
# Set comprehensive test environment
|
||||
export SRCPATH='/test/src'
|
||||
export BUILDPATH='/test/build'
|
||||
export BINPATH='/test/bin'
|
||||
export CTYPE='Release'
|
||||
export CAPPS_BUILD='ON'
|
||||
export CTOOLS_BUILD='ON'
|
||||
export CSCRIPTS='ON'
|
||||
export CMODULES='ON'
|
||||
export CBUILD_TESTING='OFF'
|
||||
export CSCRIPTPCH='ON'
|
||||
export CCOREPCH='ON'
|
||||
export CWARNINGS='ON'
|
||||
export CCOMPILERC='gcc'
|
||||
export CCOMPILERCXX='g++'
|
||||
export CCUSTOMOPTIONS='-DCUSTOM_OPTION=1'
|
||||
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
|
||||
# Change to buildpath and run configure
|
||||
cd /test || cd /tmp
|
||||
comp_configure 2>/dev/null || echo 'Configure completed with warnings'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "CMAKE_COMMAND:" ]] || [[ "$output" =~ "Configure completed" ]]
|
||||
}
|
||||
|
||||
# ===== PLATFORM SPECIFIC TESTS =====
|
||||
|
||||
@test "platform: should set correct options for detected platform" {
|
||||
# Test platform-specific CMAKE options
|
||||
run bash -c "
|
||||
# Mock cmake to capture platform-specific options
|
||||
function cmake() {
|
||||
echo 'CMAKE_PLATFORM_ARGS: $*'
|
||||
return 0
|
||||
}
|
||||
export -f cmake
|
||||
|
||||
export BUILDPATH='/tmp'
|
||||
export SRCPATH='/tmp'
|
||||
export BINPATH='/tmp'
|
||||
export CTYPE='Release'
|
||||
|
||||
source '$SCRIPT_DIR/includes/functions.sh'
|
||||
|
||||
# Change to buildpath and run configure
|
||||
cd /tmp
|
||||
comp_configure 2>/dev/null || echo 'Configure completed with warnings'
|
||||
"
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "CMAKE_PLATFORM_ARGS:" ]] || [[ "$output" =~ "Configure completed" ]]
|
||||
}
|
||||
@@ -27,7 +27,7 @@ $SUDO apt-get install -y gdbserver gdb unzip curl \
|
||||
libncurses-dev libreadline-dev clang g++ \
|
||||
gcc git cmake make ccache \
|
||||
libssl-dev libbz2-dev \
|
||||
libboost-all-dev gnupg wget jq screen tmux
|
||||
libboost-all-dev gnupg wget jq screen tmux expect
|
||||
|
||||
VAR_PATH="$CURRENT_PATH/../../../../var"
|
||||
|
||||
|
||||
@@ -31,4 +31,4 @@ if ! command -v cmake &>/dev/null ; then
|
||||
fi
|
||||
##########################################
|
||||
|
||||
brew install openssl@3 readline boost bash-completion curl unzip mysql ccache
|
||||
brew install openssl@3 readline boost bash-completion curl unzip mysql ccache expect tmux screen jq
|
||||
|
||||
@@ -32,7 +32,7 @@ $SUDO apt update
|
||||
DEBIAN_FRONTEND="noninteractive" $SUDO \
|
||||
apt-get -y install ccache clang cmake curl google-perftools libmysqlclient-dev make unzip jq screen tmux \
|
||||
libreadline-dev libncurses5-dev libncursesw5-dev libbz2-dev git gcc g++ libssl-dev \
|
||||
libncurses-dev libboost-all-dev gdb gdbserver
|
||||
libncurses-dev libboost-all-dev gdb gdbserver expect
|
||||
|
||||
VAR_PATH="$CURRENT_PATH/../../../../var"
|
||||
|
||||
|
||||
@@ -135,6 +135,9 @@ export CONFIG="/path/to/worldserver.conf"
|
||||
export SESSION_MANAGER="tmux" # none|auto|tmux|screen
|
||||
export SESSION_NAME="ac-world"
|
||||
|
||||
# Interactive mode control
|
||||
export AC_DISABLE_INTERACTIVE="0" # Set to 1 to disable interactive prompts (useful for non-interactive services)
|
||||
|
||||
# Debugging
|
||||
export GDB_ENABLED="1" # 0 or 1
|
||||
export GDB="/path/to/gdb.conf"
|
||||
@@ -254,6 +257,29 @@ Production-ready service management:
|
||||
|
||||
# Force systemd
|
||||
./service-manager.sh create world worldserver --provider systemd --bin-path /path/to/bin
|
||||
|
||||
# Create service with restart policy
|
||||
./service-manager.sh create world worldserver --bin-path /path/to/bin --restart-policy always
|
||||
```
|
||||
|
||||
#### Restart Policies
|
||||
|
||||
Services support two restart policies:
|
||||
|
||||
- **`on-failure`** (default): Restart only on crashes or errors (exit code != 0, only works with PM2 or systemd without tmux/screen)
|
||||
- **`always`**: Restart on any exit, including clean shutdown (exit code 0)
|
||||
|
||||
**Important**: When using `--restart-policy always`, the in-game command `server shutdown X` will behave like `server restart X` - the service will automatically restart after shutdown. Only the shutdown message differs from a restart message.
|
||||
|
||||
```bash
|
||||
# Service that restarts only on crashes (default behavior)
|
||||
./service-manager.sh create auth authserver --bin-path /path/to/bin --restart-policy on-failure
|
||||
|
||||
# Service that always restarts (even on manual shutdown)
|
||||
./service-manager.sh create world worldserver --bin-path /path/to/bin --restart-policy always
|
||||
|
||||
# Update existing service restart policy
|
||||
./service-manager.sh update worldserver --restart-policy always
|
||||
```
|
||||
|
||||
#### Service Operations
|
||||
@@ -293,19 +319,22 @@ Production-ready service management:
|
||||
### Method 1: Using Service Manager (Recommended)
|
||||
|
||||
```bash
|
||||
# Create multiple world server instances
|
||||
# Create multiple world server instances with different restart policies
|
||||
./service-manager.sh create world1 worldserver \
|
||||
--bin-path /path/to/bin \
|
||||
--server-config /path/to/worldserver-realm1.conf
|
||||
--server-config /path/to/worldserver-realm1.conf \
|
||||
--restart-policy on-failure
|
||||
|
||||
./service-manager.sh create world2 worldserver \
|
||||
--bin-path /path/to/bin \
|
||||
--server-config /path/to/worldserver-realm2.conf
|
||||
--server-config /path/to/worldserver-realm2.conf \
|
||||
--restart-policy always
|
||||
|
||||
# Single auth server for all realms
|
||||
# Single auth server for all realms (always restart for stability)
|
||||
./service-manager.sh create auth authserver \
|
||||
--bin-path /path/to/bin \
|
||||
--server-config /path/to/authserver.conf
|
||||
--server-config /path/to/authserver.conf \
|
||||
--restart-policy always
|
||||
```
|
||||
|
||||
### Method 2: Using Run Engine with Different Configurations
|
||||
@@ -372,6 +401,29 @@ pm2 save
|
||||
pm2 startup # Auto-start on boot
|
||||
```
|
||||
|
||||
NOTE: pm2 cannot run tmux/screen sessions, but you can always use the `attach` command to connect to the service console because pm2 supports interactive mode.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
The startup scripts recognize several environment variables for configuration and runtime behavior:
|
||||
|
||||
#### Service Detection Variables
|
||||
|
||||
- **`AC_LAUNCHED_BY_PM2`**: Set to `1` when launched by PM2 (automatically set by service-manager)
|
||||
- Disables the use of the `unbuffer` command for output capture
|
||||
- Enables non-interactive mode to prevent prompts
|
||||
- More robust than relying on PM2's internal variables
|
||||
|
||||
- **`AC_DISABLE_INTERACTIVE`**: Controls interactive mode (0=enabled, 1=disabled)
|
||||
- Automatically set based on execution context
|
||||
- Prevents AzerothCore from showing interactive prompts in service environments
|
||||
|
||||
#### Configuration Variables
|
||||
|
||||
- **`RUN_ENGINE_*`**: See [Configuration](#configuration) section for complete list
|
||||
- **`SERVICE_MODE`**: Set to `true` to enable service-specific behavior
|
||||
- **`SESSION_MANAGER`**: Override session manager choice (tmux, screen, none, auto)
|
||||
|
||||
### Systemd Services
|
||||
|
||||
When using systemd as the service provider:
|
||||
@@ -388,6 +440,11 @@ sudo systemctl status acore-auth
|
||||
sudo systemctl enable acore-auth
|
||||
```
|
||||
|
||||
**Enhanced systemd Integration:**
|
||||
- **Automatic Service Type**: When using session managers (tmux/screen), services are automatically configured with `Type=forking` for proper daemon behavior
|
||||
- **Smart ExecStop**: Services with session managers get automatic `ExecStop` commands to properly terminate tmux/screen sessions when stopping the service
|
||||
- **Non-Interactive Mode**: Services without session managers automatically set `AC_DISABLE_INTERACTIVE=1` to prevent hanging on prompts
|
||||
|
||||
### Session Management in Services
|
||||
|
||||
Services can be configured with session managers for interactive access:
|
||||
|
||||
@@ -51,7 +51,7 @@ export SCREEN_OPTIONS="${RUN_ENGINE_SCREEN_OPTIONS:-}"
|
||||
# If disabled, output will be redirected to logging files
|
||||
export WITH_CONSOLE="${RUN_ENGINE_WITH_CONSOLE:-0}"
|
||||
|
||||
# Server PID (needed when GDB_ENABLED=1)
|
||||
export SERVERPID="${RUN_ENGINE_SERVERPID:-}"
|
||||
# Restart policy (on-failure|always)
|
||||
export RESTART_POLICY="always"
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
set logging enabled on
|
||||
set debug timestamp
|
||||
run
|
||||
bt
|
||||
bt full
|
||||
info thread
|
||||
thread apply all backtrace full
|
||||
@@ -254,7 +254,7 @@ function start_service() {
|
||||
# Use environment/config values if not set from command line
|
||||
BINPATH="${BINPATH:-$RUN_ENGINE_BINPATH}"
|
||||
SERVERBIN="${SERVERBIN:-$RUN_ENGINE_SERVERBIN}"
|
||||
CONFIG="${serverconfig:-$RUN_ENGINE_CONFIG}"
|
||||
CONFIG="${serverconfig:-$CONFIG}"
|
||||
|
||||
echo "SERVERBIN: $SERVERBIN"
|
||||
|
||||
@@ -279,8 +279,9 @@ function start_service() {
|
||||
|
||||
# Set up directories and logging relative to BINPATH
|
||||
LOGS_PATH="${LOGS_PATH:-"$BINPATH/logs"}"
|
||||
CRASHES_PATH="${CRASHES_PATH:-"$BINPATH/crashes"}"
|
||||
mkdir -p "$LOGS_PATH"
|
||||
mkdir -p "$LOGS_PATH/crashes"
|
||||
mkdir -p "$CRASHES_PATH"
|
||||
else
|
||||
# For system binaries, try to detect binary location and create logs accordingly
|
||||
local detected_binpath=""
|
||||
@@ -297,12 +298,13 @@ function start_service() {
|
||||
# Set up log paths based on detected or fallback location
|
||||
if [ -n "$detected_binpath" ]; then
|
||||
LOGS_PATH="${LOGS_PATH:-"$detected_binpath/logs"}"
|
||||
CRASHES_PATH="${CRASHES_PATH:-"$detected_binpath/crashes"}"
|
||||
else
|
||||
# Fallback to current directory for logs
|
||||
LOGS_PATH="${LOGS_PATH:-./logs}"
|
||||
CRASHES_PATH="${CRASHES_PATH:-"$./crashes"}"
|
||||
fi
|
||||
|
||||
CRASHES_PATH="${CRASHES_PATH:-"$LOGS_PATH/crashes"}"
|
||||
|
||||
mkdir -p "$LOGS_PATH"
|
||||
mkdir -p "$CRASHES_PATH"
|
||||
@@ -333,6 +335,20 @@ function start_service() {
|
||||
echo "Server config: default (not specified)"
|
||||
fi
|
||||
|
||||
# Set AC_DISABLE_INTERACTIVE when running as a service without interactive session manager
|
||||
# This prevents AzerothCore from showing interactive prompts when running under systemd/pm2
|
||||
if [[ "${SERVICE_MODE:-false}" == "true" && "$session_manager" == "none" ]]; then
|
||||
export AC_DISABLE_INTERACTIVE=1
|
||||
echo "Service mode: Non-interactive mode enabled (AC_DISABLE_INTERACTIVE=1)"
|
||||
else
|
||||
export AC_DISABLE_INTERACTIVE=0
|
||||
if [[ "${SERVICE_MODE:-false}" == "true" ]]; then
|
||||
echo "Service mode: Interactive mode enabled (session manager: $session_manager)"
|
||||
else
|
||||
echo "Direct execution: Interactive mode enabled"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$use_restarter" = "true" ]; then
|
||||
# Use simple-restarter for restart functionality
|
||||
local gdb_enabled="${GDB_ENABLED:-0}"
|
||||
|
||||
@@ -97,14 +97,18 @@ function print_help() {
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --provider <type> - Service provider (pm2|systemd|auto, default: auto)"
|
||||
echo " --bin-path <path> - Path to the server binary directory (required)"
|
||||
echo " --bin-path <path> - Path to the server binary directory"
|
||||
echo " --server-config <path> - Path to the server configuration file"
|
||||
echo " --session-manager <type> - Session manager (none|tmux|screen, default: none)"
|
||||
echo " Note: PM2 doesn't support tmux/screen, always uses 'none'"
|
||||
echo " --gdb-enabled <0|1> - Enable GDB debugging (default: 0)"
|
||||
echo " --system - Create as system service (systemd only, requires sudo)"
|
||||
echo " --user - Create as user service (systemd only, default)"
|
||||
echo " --max-memory <value> - Maximum memory limit (PM2 only)"
|
||||
echo " --max-restarts <value> - Maximum restart attempts (PM2 only)"
|
||||
echo " --restart-policy <policy> - Restart policy (on-failure|always, default: always)"
|
||||
echo " on-failure: restart only on crash/error (only works with PM2 or systemd without tmux/screen)"
|
||||
echo " always: restart on any exit (including 'server shutdown')"
|
||||
echo " --no-start - Do not start the service after creation"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
@@ -123,6 +127,9 @@ function print_help() {
|
||||
echo " # Create service without starting it"
|
||||
echo " $base_name create auth authserver --bin-path /home/user/azerothcore/bin --no-start"
|
||||
echo ""
|
||||
echo " # Create service with always restart policy"
|
||||
echo " $base_name create world worldserver --bin-path /home/user/azerothcore/bin --restart-policy always"
|
||||
echo ""
|
||||
echo " # Update run-engine configuration"
|
||||
echo " $base_name update worldserver-realm1 --session-manager screen --gdb-enabled 0"
|
||||
echo ""
|
||||
@@ -137,6 +144,9 @@ function print_help() {
|
||||
echo " - Use --server-config for the actual server configuration file"
|
||||
echo " - Services use run-engine in 'start' mode for single-shot execution"
|
||||
echo " - Restart on crash is handled by PM2 or systemd, not by run-engine"
|
||||
echo " - When restart-policy is 'always': 'server shutdown X' behaves like 'server restart X'"
|
||||
echo " (only the in-game message differs, but the service will restart automatically)"
|
||||
echo " - PM2 services always use session-manager 'none' and have built-in attach functionality"
|
||||
echo " - attach command automatically detects the configured session manager and connects appropriately"
|
||||
echo " - attach always provides interactive access to the server console"
|
||||
echo " - Use 'logs' command to view service logs without interaction"
|
||||
@@ -166,9 +176,11 @@ function validate_service_exists() {
|
||||
local provider="$2"
|
||||
|
||||
if [ "$provider" = "pm2" ]; then
|
||||
# Check if service exists in PM2
|
||||
if ! pm2 id "$service_name" > /dev/null 2>&1; then
|
||||
return 1 # Service not found
|
||||
# Check if service exists in PM2 using pm2 describe (most reliable)
|
||||
if pm2 describe "$service_name" >/dev/null 2>&1; then
|
||||
return 0 # Service exists
|
||||
else
|
||||
return 1 # Service doesn't exist
|
||||
fi
|
||||
elif [ "$provider" = "systemd" ]; then
|
||||
# Check if service exists in systemd
|
||||
@@ -249,7 +261,8 @@ function get_service_info() {
|
||||
function pm2_create_service() {
|
||||
local service_name="$1"
|
||||
local command="$2"
|
||||
shift 2
|
||||
local restart_policy="$3"
|
||||
shift 3
|
||||
|
||||
check_pm2 || return 1
|
||||
|
||||
@@ -275,8 +288,18 @@ function pm2_create_service() {
|
||||
esac
|
||||
done
|
||||
|
||||
# Build PM2 start command
|
||||
local pm2_cmd="pm2 start '$command$additional_args' --name '$service_name'"
|
||||
|
||||
# Set stop exit codes based on restart policy
|
||||
local stop_exit_codes=""
|
||||
if [ "$restart_policy" = "always" ]; then
|
||||
# PM2 will restart on any exit code (including 0)
|
||||
stop_exit_codes=""
|
||||
else
|
||||
# PM2 will not restart on clean shutdown (exit code 0)
|
||||
stop_exit_codes=" --stop-exit-codes 0"
|
||||
fi
|
||||
# Build PM2 start command with AzerothCore environment variable
|
||||
local pm2_cmd="AC_LAUNCHED_BY_PM2=1 pm2 start '$command$additional_args' --name '$service_name'$stop_exit_codes"
|
||||
|
||||
# Add memory limit if specified
|
||||
if [ -n "$max_memory" ]; then
|
||||
@@ -301,6 +324,7 @@ function pm2_create_service() {
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function pm2_remove_service() {
|
||||
local service_name="$1"
|
||||
|
||||
@@ -309,12 +333,24 @@ function pm2_remove_service() {
|
||||
echo -e "${YELLOW}Stopping and removing PM2 service: $service_name${NC}"
|
||||
|
||||
# Stop the service if it's running
|
||||
if pm2 id "$service_name" > /dev/null 2>&1; then
|
||||
if pm2 describe "$service_name" >/dev/null 2>&1; then
|
||||
pm2 stop "$service_name" 2>/dev/null || true
|
||||
pm2 delete "$service_name" 2>/dev/null
|
||||
pm2 delete "$service_name" 2>/dev/null
|
||||
|
||||
# Wait for PM2 to process the stop/delete command with timeout
|
||||
local timeout=10
|
||||
local elapsed=0
|
||||
while pm2 describe "$service_name" >/dev/null 2>&1; do
|
||||
if [ "$elapsed" -ge "$timeout" ]; then
|
||||
echo -e "${RED}Timeout reached while waiting for PM2 service '$service_name' to stop${NC}"
|
||||
return 1
|
||||
fi
|
||||
sleep 0.5
|
||||
elapsed=$((elapsed + 1))
|
||||
done
|
||||
|
||||
# Verify the service was removed
|
||||
if pm2 id "$service_name" > /dev/null 2>&1; then
|
||||
if pm2 describe "$service_name" >/dev/null 2>&1; then
|
||||
echo -e "${RED}Failed to remove PM2 service '$service_name'${NC}"
|
||||
return 1
|
||||
fi
|
||||
@@ -365,8 +401,9 @@ function get_systemd_dir() {
|
||||
function systemd_create_service() {
|
||||
local service_name="$1"
|
||||
local command="$2"
|
||||
local restart_policy="$3"
|
||||
local systemd_type="--user"
|
||||
shift 2
|
||||
shift 3
|
||||
|
||||
check_systemd || return 1
|
||||
|
||||
@@ -398,6 +435,25 @@ function systemd_create_service() {
|
||||
mkdir -p "$systemd_dir"
|
||||
fi
|
||||
|
||||
# Determine service type and ExecStop for systemd
|
||||
local service_type="simple"
|
||||
local exec_stop=""
|
||||
|
||||
# Load the run-engine config to check the session manager
|
||||
local run_engine_config_path="$CONFIG_DIR/$service_name-run-engine.conf"
|
||||
local session_manager="none"
|
||||
local session_name="$service_name"
|
||||
|
||||
if [ -f "$run_engine_config_path" ]; then
|
||||
# Read the session manager and name from the config file without sourcing it
|
||||
session_manager=$(grep -oP 'SESSION_MANAGER="\K[^"]+' "$run_engine_config_path" || echo "none")
|
||||
session_name=$(grep -oP 'SESSION_NAME="\K[^"]+' "$run_engine_config_path" || echo "$service_name")
|
||||
fi
|
||||
|
||||
if [ "$session_manager" = "tmux" ] || [ "$session_manager" = "screen" ]; then
|
||||
service_type="forking"
|
||||
fi
|
||||
|
||||
# Create service file
|
||||
echo -e "${YELLOW}Creating systemd service: $service_name${NC}"
|
||||
|
||||
@@ -409,9 +465,9 @@ Description=AzerothCore $service_name
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
Type=${service_type}
|
||||
ExecStart=$command
|
||||
Restart=always
|
||||
Restart=$restart_policy
|
||||
RestartSec=3
|
||||
User=$(whoami)
|
||||
Group=$(id -gn)
|
||||
@@ -430,9 +486,9 @@ Description=AzerothCore $service_name
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
Type=${service_type}
|
||||
ExecStart=$command
|
||||
Restart=always
|
||||
Restart=$restart_policy
|
||||
RestartSec=3
|
||||
WorkingDirectory=$(realpath "$bin_path")
|
||||
StandardOutput=journal+console
|
||||
@@ -536,7 +592,19 @@ function systemd_service_action() {
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}${action^} systemd service: $service_name${NC}"
|
||||
|
||||
|
||||
# stop tmux or screen session if applicable && action is stop or restart
|
||||
if [[ "$action" == "stop" || "$action" == "restart" ]]; then
|
||||
local session_manager=$(grep -oP 'SESSION_MANAGER="\K[^"]+' "$CONFIG_DIR/$service_name-run-engine.conf" || echo "none")
|
||||
if [ "$session_manager" = "tmux" ]; then
|
||||
echo -e "${YELLOW}Stopping tmux session for service: $service_name${NC}"
|
||||
tmux kill-session -t "$service_name"
|
||||
elif [ "$session_manager" = "screen" ]; then
|
||||
echo -e "${YELLOW}Stopping screen session for service: $service_name${NC}"
|
||||
screen -S "$service_name" -X quit
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$systemd_type" = "--system" ]; then
|
||||
systemctl "$action" "$service_name.service"
|
||||
else
|
||||
@@ -595,6 +663,7 @@ function create_service() {
|
||||
local server_config=""
|
||||
local session_manager="none"
|
||||
local gdb_enabled="0"
|
||||
local restart_policy="always"
|
||||
local systemd_type="--user"
|
||||
local pm2_opts=""
|
||||
local auto_start="true"
|
||||
@@ -622,6 +691,10 @@ function create_service() {
|
||||
gdb_enabled="$2"
|
||||
shift 2
|
||||
;;
|
||||
--restart-policy)
|
||||
restart_policy="$2"
|
||||
shift 2
|
||||
;;
|
||||
--system)
|
||||
systemd_type="--system"
|
||||
shift
|
||||
@@ -658,14 +731,34 @@ function create_service() {
|
||||
echo -e "${RED}Error: Invalid provider. Use 'pm2', 'systemd', or 'auto'${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Validate restart policy
|
||||
if [[ "$restart_policy" != "on-failure" && "$restart_policy" != "always" ]]; then
|
||||
echo -e "${RED}Error: Invalid restart policy. Use 'on-failure' or 'always'${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# PM2 specific validation and adjustments
|
||||
if [ "$provider" = "pm2" ]; then
|
||||
# PM2 doesn't support session managers (tmux/screen), force to none
|
||||
if [ "$session_manager" != "none" ]; then
|
||||
echo -e "${YELLOW}Warning: PM2 doesn't support session managers. Setting session-manager to 'none'${NC}"
|
||||
echo -e "${BLUE}PM2 has built-in attach functionality via: $0 attach $service_name${NC}"
|
||||
session_manager="none"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Determine server binary based on service type
|
||||
local server_bin="${service_type}server"
|
||||
local server_binary_path=$(realpath "$bin_path/$server_bin")
|
||||
local real_config_path=""
|
||||
if [ -n "$server_config" ]; then
|
||||
real_config_path=$(realpath "$server_config")
|
||||
fi
|
||||
|
||||
# Check if binary exists
|
||||
if [ ! -f "$server_binary_path" ]; then
|
||||
echo -e "${RED}Error: Server binary not found: $server_binary_path${NC}"
|
||||
echo -e "${RED}Error: Server binary not found: $server_binary_path, please check your --bin-path option ${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -681,6 +774,13 @@ export GDB_ENABLED=$gdb_enabled
|
||||
# Session manager (none|auto|tmux|screen)
|
||||
export SESSION_MANAGER="$session_manager"
|
||||
|
||||
# Restart policy (on-failure|always)
|
||||
export RESTART_POLICY="$restart_policy"
|
||||
|
||||
# Service mode - indicates this is running under a service manager (systemd/pm2)
|
||||
# When true, AC_DISABLE_INTERACTIVE will be set if no interactive session manager is used
|
||||
export SERVICE_MODE="true"
|
||||
|
||||
# Session name for tmux/screen (optional)
|
||||
export SESSION_NAME="${service_name}"
|
||||
|
||||
@@ -691,7 +791,7 @@ export BINPATH="$bin_path"
|
||||
export SERVERBIN="$server_bin"
|
||||
|
||||
# Server configuration file path
|
||||
export CONFIG="$server_config"
|
||||
export CONFIG="$real_config_path"
|
||||
|
||||
# Show console output for easier debugging
|
||||
export WITH_CONSOLE=1
|
||||
@@ -707,6 +807,9 @@ EOF
|
||||
# run-engine configuration file
|
||||
RUN_ENGINE_CONFIG_FILE="$run_engine_config"
|
||||
|
||||
# Restart policy
|
||||
RESTART_POLICY="$restart_policy"
|
||||
|
||||
# Provider-specific options
|
||||
SYSTEMD_TYPE="$systemd_type"
|
||||
PM2_OPTS="$pm2_opts"
|
||||
@@ -719,17 +822,17 @@ EOF
|
||||
local service_creation_success=false
|
||||
if [ "$provider" = "pm2" ]; then
|
||||
if [ -n "$pm2_opts" ]; then
|
||||
if pm2_create_service "$service_name" "$run_engine_cmd" $pm2_opts; then
|
||||
if pm2_create_service "$service_name" "$run_engine_cmd" "$restart_policy" $pm2_opts; then
|
||||
service_creation_success=true
|
||||
fi
|
||||
else
|
||||
if pm2_create_service "$service_name" "$run_engine_cmd"; then
|
||||
if pm2_create_service "$service_name" "$run_engine_cmd" "$restart_policy"; then
|
||||
service_creation_success=true
|
||||
fi
|
||||
fi
|
||||
|
||||
elif [ "$provider" = "systemd" ]; then
|
||||
if systemd_create_service "$service_name" "$run_engine_cmd" "$systemd_type"; then
|
||||
if systemd_create_service "$service_name" "$run_engine_cmd" "$restart_policy" "$systemd_type"; then
|
||||
service_creation_success=true
|
||||
fi
|
||||
fi
|
||||
@@ -811,6 +914,11 @@ function update_service() {
|
||||
config_updated=true
|
||||
shift 2
|
||||
;;
|
||||
--restart-policy)
|
||||
export RESTART_POLICY="$2"
|
||||
config_updated=true
|
||||
shift 2
|
||||
;;
|
||||
--system)
|
||||
SYSTEMD_TYPE="--system"
|
||||
shift
|
||||
@@ -830,6 +938,20 @@ function update_service() {
|
||||
esac
|
||||
done
|
||||
|
||||
# PM2 specific validation for session manager
|
||||
if [ "$provider" = "pm2" ] && [ -n "$SESSION_MANAGER" ] && [ "$SESSION_MANAGER" != "none" ]; then
|
||||
echo -e "${YELLOW}Warning: PM2 doesn't support session managers. Setting session-manager to 'none'${NC}"
|
||||
echo -e "${BLUE}PM2 has built-in attach functionality via: $0 attach $service_name${NC}"
|
||||
export SESSION_MANAGER="none"
|
||||
config_updated=true
|
||||
fi
|
||||
|
||||
# Validate restart policy if provided
|
||||
if [ -n "$RESTART_POLICY" ] && [[ "$RESTART_POLICY" != "on-failure" && "$RESTART_POLICY" != "always" ]]; then
|
||||
echo -e "${RED}Error: Invalid restart policy. Use 'on-failure' or 'always'${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$config_updated" = "true" ]; then
|
||||
# Update run-engine configuration file
|
||||
cat > "$RUN_ENGINE_CONFIG_FILE" << EOF
|
||||
@@ -842,6 +964,12 @@ export GDB_ENABLED=${GDB_ENABLED:-0}
|
||||
# Session manager (none|auto|tmux|screen)
|
||||
export SESSION_MANAGER="${SESSION_MANAGER:-none}"
|
||||
|
||||
# Restart policy (on-failure|always)
|
||||
export RESTART_POLICY="${RESTART_POLICY:-on-failure}"
|
||||
|
||||
# Service mode - indicates this is running under a service manager (systemd/pm2)
|
||||
export SERVICE_MODE="true"
|
||||
|
||||
# Session name for tmux/screen
|
||||
export SESSION_NAME="${service_name}"
|
||||
|
||||
@@ -871,6 +999,9 @@ EOF
|
||||
# run-engine configuration file
|
||||
RUN_ENGINE_CONFIG_FILE="$RUN_ENGINE_CONFIG_FILE"
|
||||
|
||||
# Restart policy
|
||||
RESTART_POLICY="${RESTART_POLICY:-on-failure}"
|
||||
|
||||
# Provider-specific options
|
||||
SYSTEMD_TYPE="$SYSTEMD_TYPE"
|
||||
PM2_OPTS="$PM2_OPTS"
|
||||
@@ -940,15 +1071,6 @@ function list_services() {
|
||||
return
|
||||
fi
|
||||
|
||||
# Show PM2 services
|
||||
if [ -z "$provider_filter" ] || [ "$provider_filter" = "pm2" ]; then
|
||||
local pm2_services=$(jq -r '.[] | select(.provider == "pm2") | .name' "$REGISTRY_FILE" 2>/dev/null)
|
||||
if [ -n "$pm2_services" ] && command -v pm2 >/dev/null 2>&1; then
|
||||
echo -e "\n${YELLOW}PM2 Services:${NC}"
|
||||
pm2 list
|
||||
fi
|
||||
fi
|
||||
|
||||
# Show systemd services
|
||||
if [ -z "$provider_filter" ] || [ "$provider_filter" = "systemd" ]; then
|
||||
local systemd_services=$(jq -r '.[] | select(.provider == "systemd") | .name' "$REGISTRY_FILE" 2>/dev/null)
|
||||
@@ -978,6 +1100,15 @@ function list_services() {
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Show PM2 services
|
||||
if [ -z "$provider_filter" ] || [ "$provider_filter" = "pm2" ]; then
|
||||
local pm2_services=$(jq -r '.[] | select(.provider == "pm2") | .name' "$REGISTRY_FILE" 2>/dev/null)
|
||||
if [ -n "$pm2_services" ] && command -v pm2 >/dev/null 2>&1; then
|
||||
echo -e "\n${YELLOW}PM2 Services:${NC}"
|
||||
pm2 list
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function service_action() {
|
||||
@@ -1079,33 +1210,67 @@ function attach_to_service() {
|
||||
source "$RUN_ENGINE_CONFIG_FILE"
|
||||
|
||||
# Auto-detect session manager and attach accordingly
|
||||
case "$SESSION_MANAGER" in
|
||||
"tmux")
|
||||
attach_tmux_session "$service_name" "$provider"
|
||||
;;
|
||||
"screen")
|
||||
attach_screen_session "$service_name" "$provider"
|
||||
;;
|
||||
"none"|"auto"|*)
|
||||
# No session manager - launch interactive shell directly
|
||||
attach_interactive_shell "$service_name" "$provider"
|
||||
;;
|
||||
esac
|
||||
if [ "$provider" = "pm2" ]; then
|
||||
# PM2 has built-in attach functionality
|
||||
attach_pm2_process "$service_name"
|
||||
else
|
||||
# For systemd, check session manager
|
||||
case "$SESSION_MANAGER" in
|
||||
"tmux")
|
||||
attach_tmux_session "$service_name" "$provider"
|
||||
;;
|
||||
"screen")
|
||||
attach_screen_session "$service_name" "$provider"
|
||||
;;
|
||||
"none"|"auto"|*)
|
||||
# No session manager - show helpful message for systemd
|
||||
attach_interactive_shell "$service_name" "$provider"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
function attach_pm2_process() {
|
||||
local service_name="$1"
|
||||
|
||||
|
||||
# First check if the service exists and get its ID
|
||||
local pm2_id=$(pm2 id "$service_name" 2>/dev/null)
|
||||
if [ -z "$pm2_id" ] || [ "$pm2_id" = "[]" ]; then
|
||||
echo -e "${RED}Error: PM2 process '$service_name' not found${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract the numeric ID from the JSON response
|
||||
local numeric_id=$(echo "$pm2_id" | jq -r '.[0] // empty')
|
||||
if [ -z "$numeric_id" ]; then
|
||||
echo -e "${RED}Error: Could not determine PM2 process ID for '$service_name'${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}Attaching to PM2 process: $service_name (ID: $numeric_id)${NC}"
|
||||
pm2 attach "$numeric_id"
|
||||
}
|
||||
|
||||
function attach_interactive_shell() {
|
||||
local service_name="$1"
|
||||
local provider="$2"
|
||||
|
||||
# Get service info again to access configuration
|
||||
# For PM2, use PM2's attach functionality
|
||||
if [ "$provider" = "pm2" ]; then
|
||||
attach_pm2_process "$service_name"
|
||||
return $?
|
||||
fi
|
||||
|
||||
# For systemd without session manager, show helpful message
|
||||
local service_info=$(get_service_info "$service_name")
|
||||
local config_file=$(echo "$service_info" | jq -r '.config')
|
||||
|
||||
source "$config_file"
|
||||
source "$RUN_ENGINE_CONFIG_FILE"
|
||||
|
||||
echo -e "${RED}Error: Cannot attach to service '$service_name'${NC} [for now]"
|
||||
echo -e "${YELLOW}Interactive attachment requires a session manager (tmux or screen).${NC}"
|
||||
echo -e "${RED}Error: Cannot attach to systemd service '$service_name'${NC}"
|
||||
echo -e "${YELLOW}Interactive attachment for systemd requires a session manager (tmux or screen).${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}Current session manager: $SESSION_MANAGER${NC}"
|
||||
echo ""
|
||||
@@ -1145,9 +1310,7 @@ function attach_tmux_session() {
|
||||
else
|
||||
echo -e "${RED}Error: tmux session '$tmux_session' not found${NC}"
|
||||
echo -e "${YELLOW}Available tmux sessions:${NC}"
|
||||
tmux list-sessions 2>/dev/null || echo "No active tmux sessions"
|
||||
echo -e "${BLUE}Starting new interactive session instead...${NC}"
|
||||
attach_interactive_shell "$service_name" "$provider"
|
||||
tmux list-sessions 2>/dev/null || echo "No active tmux sessions (is it stopped or restarting?)"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -1173,9 +1336,7 @@ function attach_screen_session() {
|
||||
else
|
||||
echo -e "${RED}Error: screen session '$screen_session' not found${NC}"
|
||||
echo -e "${YELLOW}Available screen sessions:${NC}"
|
||||
screen -list 2>/dev/null || echo "No active screen sessions"
|
||||
echo -e "${BLUE}Starting new interactive session instead...${NC}"
|
||||
attach_interactive_shell "$service_name" "$provider"
|
||||
screen -list 2>/dev/null || echo "No active screen sessions (is it stopped or restarting?)"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,9 @@ CRASHES_PATH="$8"
|
||||
BINARY="$BINPATH/$BINFILE"
|
||||
|
||||
# Default values (same as starter)
|
||||
DEFAULT_CRASHES_PATH="./crashes"
|
||||
DEFAULT_GDB_FILE="$CURRENT_PATH/gdb.conf"
|
||||
|
||||
# Set defaults if not provided
|
||||
CRASHES_PATH="${CRASHES_PATH:-$DEFAULT_CRASHES_PATH}"
|
||||
GDB_FILE="${GDB_FILE:-$DEFAULT_GDB_FILE}"
|
||||
|
||||
# Counters for crash detection
|
||||
|
||||
@@ -3,16 +3,17 @@
|
||||
# AzerothCore Starter Script
|
||||
# This script handles the execution of AzerothCore binaries with optional GDB support
|
||||
#
|
||||
# Usage: starter <binary> [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]
|
||||
# Usage: starter <binpath> <binfile> [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]
|
||||
#
|
||||
# Parameters:
|
||||
# $1 - Binary to execute (required)
|
||||
# $2 - GDB configuration file (optional)
|
||||
# $3 - Configuration file path (optional)
|
||||
# $4 - System log file (optional)
|
||||
# $5 - System error file (optional)
|
||||
# $6 - GDB enabled flag (0/1, optional)
|
||||
# $7 - Crashes directory path (optional)
|
||||
# $1 - Binary path (required)
|
||||
# $2 - Binary file name (required)
|
||||
# $3 - GDB configuration file (optional)
|
||||
# $4 - Configuration file path (optional)
|
||||
# $5 - System log file (optional)
|
||||
# $6 - System error file (optional)
|
||||
# $7 - GDB enabled flag (0/1, optional)
|
||||
# $8 - Crashes directory path (optional)
|
||||
|
||||
BINPATH="$1"
|
||||
BINFILE="$2"
|
||||
@@ -23,25 +24,22 @@ SYSERR="$6"
|
||||
GDB_ENABLED="${7:-0}"
|
||||
CRASHES_PATH="$8"
|
||||
|
||||
BINARY=$(realpath "$BINPATH/$BINFILE")
|
||||
|
||||
# Default values
|
||||
CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DEFAULT_CRASHES_PATH="$CURRENT_PATH/logs/crashes"
|
||||
DEFAULT_GDB_FILE="$CURRENT_PATH/gdb.conf"
|
||||
DEFAULT_CRASHES_PATH=$(realpath "$BINPATH/crashes")
|
||||
[ -n "$CONFIG" ] && CONFIG_ABS=$(realpath "$CONFIG")
|
||||
|
||||
# Set defaults if not provided
|
||||
CONFIG="${CONFIG:-""}"
|
||||
CRASHES_PATH="${CRASHES_PATH:-$DEFAULT_CRASHES_PATH}"
|
||||
GDB_FILE="${GDB_FILE:-$DEFAULT_GDB_FILE}"
|
||||
|
||||
# Validate binary
|
||||
if [ -z "$BINARY" ]; then
|
||||
echo "Error: Binary parameter is required"
|
||||
echo "Usage: $0 <binary> [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]"
|
||||
if [ -z "$BINPATH" ] || [ -z "$BINFILE" ]; then
|
||||
echo "Error: Binary path and file are required"
|
||||
echo "Usage: $0 <binpath> <binfile> [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BINARY="$BINPATH/$BINFILE"
|
||||
if [ ! -f "$BINARY" ]; then
|
||||
echo "Error: Binary '$BINARY' not found"
|
||||
exit 1
|
||||
@@ -50,7 +48,7 @@ fi
|
||||
# Create crashes directory if it doesn't exist
|
||||
mkdir -p "$CRASHES_PATH"
|
||||
|
||||
cd $BINPATH || {
|
||||
cd "$BINPATH" || {
|
||||
echo "Error: Could not change to binary path '$BINPATH'"
|
||||
exit 1
|
||||
}
|
||||
@@ -59,22 +57,25 @@ EXECPATH=$(realpath "$BINFILE")
|
||||
|
||||
if [ "$GDB_ENABLED" -eq 1 ]; then
|
||||
echo "Starting $EXECPATH with GDB enabled"
|
||||
|
||||
|
||||
# Generate GDB configuration on the fly
|
||||
TIMESTAMP=$(date +%Y-%m-%d-%H-%M-%S)
|
||||
GDB_TEMP_FILE="$CRASHES_PATH/gdb-$TIMESTAMP.conf"
|
||||
GDB_OUTPUT_FILE="$CRASHES_PATH/gdb-$TIMESTAMP.txt"
|
||||
|
||||
# Create GDB configuration
|
||||
cat > "$GDB_TEMP_FILE" << EOF
|
||||
|
||||
# Create GDB configuration file if it is not defined
|
||||
if [ -z "$GDB_FILE" ]; then
|
||||
|
||||
# Create GDB configuration
|
||||
cat > "$GDB_TEMP_FILE" << EOF
|
||||
set logging file $GDB_OUTPUT_FILE
|
||||
set logging enabled on
|
||||
set debug timestamp
|
||||
EOF
|
||||
|
||||
# Add run command with config if specified
|
||||
if [ -n "$CONFIG" ]; then
|
||||
echo "run -c $CONFIG" >> "$GDB_TEMP_FILE"
|
||||
if [ -n "$CONFIG_ABS" ]; then
|
||||
echo "run -c $CONFIG_ABS" >> "$GDB_TEMP_FILE"
|
||||
else
|
||||
echo "run" >> "$GDB_TEMP_FILE"
|
||||
fi
|
||||
@@ -86,32 +87,51 @@ info thread
|
||||
thread apply all backtrace full
|
||||
EOF
|
||||
|
||||
|
||||
GDB_FILE="$GDB_TEMP_FILE"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Create log files if specified
|
||||
if [ -n "$SYSLOG" ]; then
|
||||
[ ! -f "$SYSLOG" ] && touch "$SYSLOG"
|
||||
fi
|
||||
|
||||
if [ -n "$SYSERR" ]; then
|
||||
[ ! -f "$SYSERR" ] && touch "$SYSERR"
|
||||
fi
|
||||
|
||||
# Execute with GDB
|
||||
if [ "${WITH_CONSOLE:-0}" -eq 0 ] && [ -n "$SYSLOG" ] && [ -n "$SYSERR" ]; then
|
||||
gdb -x "$GDB_TEMP_FILE" --batch "$EXECPATH" >> "$SYSLOG" 2>> "$SYSERR"
|
||||
gdb -x "$GDB_FILE" --batch "$EXECPATH" >> "$SYSLOG" 2>> "$SYSERR"
|
||||
else
|
||||
echo "> Console enabled"
|
||||
if [ -n "$SYSLOG" ] && [ -n "$SYSERR" ]; then
|
||||
gdb -x "$GDB_TEMP_FILE" --batch "$EXECPATH" > >(tee "$SYSLOG") 2> >(tee "$SYSERR" >&2)
|
||||
gdb -x "$GDB_FILE" --batch "$EXECPATH" > >(tee "$SYSLOG") 2> >(tee "$SYSERR" >&2)
|
||||
else
|
||||
gdb -x "$GDB_TEMP_FILE" --batch "$EXECPATH"
|
||||
gdb -x "$GDB_FILE" --batch "$EXECPATH"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Cleanup temporary GDB file
|
||||
rm -f "$GDB_TEMP_FILE"
|
||||
else
|
||||
if [ -n "$CONFIG" ]; then
|
||||
script -q -e -c "$EXECPATH -c \"$CONFIG\""
|
||||
else
|
||||
script -q -e -c "$EXECPATH"
|
||||
|
||||
|
||||
# clean up temporary GDB file if it exists
|
||||
if [ -n "$GDB_TEMP_FILE" ]; then
|
||||
# Clean up temporary GDB file
|
||||
rm -f "$GDB_TEMP_FILE"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Starting $BINFILE without GDB"
|
||||
if [[ "$AC_LAUNCHED_BY_PM2" == "1" ]]; then
|
||||
echo "Running under PM2"
|
||||
"$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"}
|
||||
else
|
||||
if command -v unbuffer >/dev/null 2>&1; then
|
||||
export AC_DISABLE_INTERACTIVE=0
|
||||
unbuffer "$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"}
|
||||
else
|
||||
echo "⚠️ unbuffer not found, the output may not be line-buffered. Try installing expect."
|
||||
exec "$EXECPATH" ${CONFIG_ABS:+-c "$CONFIG_ABS"}
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -23,7 +23,7 @@ teardown() {
|
||||
@test "starter: should fail with missing parameters" {
|
||||
run timeout 3s "$SCRIPT_DIR/starter" '' ''
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" =~ "Error: Binary '/' not found" ]]
|
||||
[[ "$output" =~ "Error: Binary path and file are required" ]]
|
||||
}
|
||||
|
||||
@test "starter: should start with valid binary" {
|
||||
@@ -38,7 +38,16 @@ teardown() {
|
||||
@test "starter: should validate binary path exists" {
|
||||
run "$SCRIPT_DIR/starter" "/nonexistent/path" "test-server"
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" =~ "Binary parameter is required" ]] || [[ "$output" =~ "No such file or directory" ]]
|
||||
[[ "$output" =~ "Binary '/nonexistent/path/test-server' not found" ]]
|
||||
}
|
||||
|
||||
@test "starter: should detect PM2 environment properly" {
|
||||
cd "$TEST_DIR"
|
||||
# Test with AC_LAUNCHED_BY_PM2=1 (should not use script command)
|
||||
AC_LAUNCHED_BY_PM2=1 run timeout 5s "$SCRIPT_DIR/starter" "$TEST_DIR/bin" "test-server" "" "$TEST_DIR/test-server.conf" "" "" 0
|
||||
debug_on_failure
|
||||
# Should start without using script command
|
||||
[[ "$output" =~ "Test server starting" ]]
|
||||
}
|
||||
|
||||
# ===== SIMPLE RESTARTER TESTS =====
|
||||
@@ -46,7 +55,7 @@ teardown() {
|
||||
@test "simple-restarter: should fail with missing parameters" {
|
||||
run timeout 3s "$SCRIPT_DIR/simple-restarter" '' ''
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" =~ "Error: Binary '/' not found" ]]
|
||||
[[ "$output" =~ "Error: Binary path and file are required" ]]
|
||||
}
|
||||
|
||||
@test "simple-restarter: should fail with missing binary" {
|
||||
@@ -109,6 +118,31 @@ teardown() {
|
||||
[[ "$output" =~ "Missing required arguments" ]] || [[ "$output" =~ "Error:" ]]
|
||||
}
|
||||
|
||||
@test "service-manager: should validate restart policy values" {
|
||||
run "$SCRIPT_DIR/service-manager.sh" create auth test-auth --bin-path /nonexistent --restart-policy invalid
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" =~ "Invalid restart policy" ]]
|
||||
}
|
||||
|
||||
@test "service-manager: should accept valid restart policy values" {
|
||||
# Test on-failure (should be accepted)
|
||||
run "$SCRIPT_DIR/service-manager.sh" create auth test-auth --bin-path /nonexistent --restart-policy on-failure
|
||||
# Should fail due to missing binary, not restart policy validation
|
||||
[[ ! "$output" =~ "Invalid restart policy" ]]
|
||||
|
||||
# Test always (should be accepted)
|
||||
run "$SCRIPT_DIR/service-manager.sh" create auth test-auth2 --bin-path /nonexistent --restart-policy always
|
||||
# Should fail due to missing binary, not restart policy validation
|
||||
[[ ! "$output" =~ "Invalid restart policy" ]]
|
||||
}
|
||||
|
||||
@test "service-manager: should include restart policy in help output" {
|
||||
run "$SCRIPT_DIR/service-manager.sh" help
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "--restart-policy" ]]
|
||||
[[ "$output" =~ "on-failure|always" ]]
|
||||
}
|
||||
|
||||
# ===== EXAMPLE SCRIPTS TESTS =====
|
||||
|
||||
@test "examples: restarter-world should show configuration error" {
|
||||
|
||||
@@ -7,6 +7,18 @@
|
||||
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'
|
||||
@@ -28,6 +40,7 @@ show_help() {
|
||||
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 <num> Set number of parallel jobs (default: $ACORE_TEST_CORES)"
|
||||
echo " --dir <path> Run tests in specific directory"
|
||||
echo " --all Run all tests in all modules"
|
||||
echo ""
|
||||
@@ -103,6 +116,17 @@ while [[ $# -gt 0 ]]; do
|
||||
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")
|
||||
@@ -234,7 +258,7 @@ if [[ "$COUNT_ONLY" == true ]]; then
|
||||
fi
|
||||
|
||||
# Build BATS command
|
||||
BATS_CMD="bats"
|
||||
BATS_CMD="bats --jobs $ACORE_TEST_CORES"
|
||||
|
||||
# Set output format
|
||||
if [[ "$TAP" == true ]]; then
|
||||
@@ -256,7 +280,7 @@ fi
|
||||
# Add test files
|
||||
BATS_CMD+=" ${TEST_FILES[*]}"
|
||||
|
||||
echo -e "${BLUE}Running AzerothCore Tests${NC}"
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user