pipeline { agent any environment { // 环境变量 PYTHON_VERSION = '3.9' VENV_NAME = 'venv' TEST_REPORTS_DIR = 'test-reports' COVERAGE_DIR = 'coverage-reports' // 设置Python模块路径 PYTHONPATH = "${WORKSPACE}" } stages { stage('Preparation') { steps { script { // 清理工作空间 echo 'Cleaning workspace...' deleteDir() // 检出代码 checkout scm // 创建报告目录 sh """ mkdir -p ${TEST_REPORTS_DIR} mkdir -p ${COVERAGE_DIR} """ } } } stage('Environment Setup') { steps { script { echo 'Setting up Python environment...' sh """ # 创建虚拟环境 python${PYTHON_VERSION} -m venv ${VENV_NAME} # 激活虚拟环境并安装依赖 . ${VENV_NAME}/bin/activate pip config set global.index-url https://mirrors.ustc.edu.cn/pypi/simple # 设置PYTHONPATH export PYTHONPATH=\${PWD}:\$PYTHONPATH # 升级pip pip install --upgrade pip # 安装项目依赖 pip install -r requirements.txt # 安装测试依赖 pip install -r requirements-test.txt # 验证FFmpeg可用性 which ffmpeg || echo "Warning: FFmpeg not found, integration tests may be skipped" """ } } } stage('Code Quality Check') { parallel { stage('Linting') { steps { script { echo 'Running code linting...' sh """ . ${VENV_NAME}/bin/activate export PYTHONPATH=\${PWD}:\$PYTHONPATH # 运行flake8检查(使用项目配置.flake8) flake8 entity/ services/ --output-file=${TEST_REPORTS_DIR}/flake8-report.txt --tee || true # 运行black格式检查 black --check --diff --line-length 88 entity/ services/ > ${TEST_REPORTS_DIR}/black-report.txt || true """ } } } stage('Type Checking') { steps { script { echo 'Running type checking with mypy...' sh """ . ${VENV_NAME}/bin/activate export PYTHONPATH=\${PWD}:\$PYTHONPATH # 运行mypy类型检查 mypy --explicit-package-bases services/ entity/ > ${TEST_REPORTS_DIR}/mypy-report.txt || true """ } } } } post { always { // 发布代码质量报告(等待所有并行任务完成后统一发布) publishHTML([ allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: TEST_REPORTS_DIR, reportFiles: 'flake8-report.txt,black-report.txt,mypy-report.txt', reportName: 'Code Quality Report' ]) } } } stage('Unit Tests') { steps { script { echo 'Running unit tests...' sh """ . ${VENV_NAME}/bin/activate export PYTHONPATH=\${PWD}:\$PYTHONPATH # 运行单元测试 pytest tests/test_effects/ tests/test_ffmpeg_builder/ \\ --junitxml=${TEST_REPORTS_DIR}/unit-tests.xml \\ --html=${TEST_REPORTS_DIR}/unit-tests.html \\ --self-contained-html \\ --cov=entity \\ --cov=services \\ --cov-report=xml:${COVERAGE_DIR}/unit-coverage.xml \\ --cov-branch \\ -v \\ -m "not integration" """ } } post { always { // 发布单元测试结果 junit "${TEST_REPORTS_DIR}/unit-tests.xml" // 发布HTML测试报告 publishHTML([ allowMissing: false, alwaysLinkToLastBuild: true, keepAll: true, reportDir: TEST_REPORTS_DIR, reportFiles: 'unit-tests.html', reportName: 'Unit Tests Report' ]) } } } stage('Integration Tests') { when { // 只在有FFmpeg的环境中运行集成测试 expression { return sh( script: 'which ffmpeg', returnStatus: true ) == 0 } } steps { script { echo 'Running integration tests...' sh """ . ${VENV_NAME}/bin/activate export PYTHONPATH=\${PWD}:\$PYTHONPATH # 运行集成测试 pytest tests/test_integration/ \\ --junitxml=${TEST_REPORTS_DIR}/integration-tests.xml \\ --html=${TEST_REPORTS_DIR}/integration-tests.html \\ --self-contained-html \\ --cov=entity \\ --cov=services \\ --cov-report=xml:${COVERAGE_DIR}/integration-coverage.xml \\ --cov-branch \\ --timeout=300 \\ -v \\ -m "integration" """ } } post { always { // 发布集成测试结果 junit "${TEST_REPORTS_DIR}/integration-tests.xml" // 发布HTML集成测试报告 publishHTML([ allowMissing: false, alwaysLinkToLastBuild: true, keepAll: true, reportDir: TEST_REPORTS_DIR, reportFiles: 'integration-tests.html', reportName: 'Integration Tests Report' ]) } } } stage('Complete Test Suite') { steps { script { echo 'Running complete test suite with coverage...' sh """ . ${VENV_NAME}/bin/activate export PYTHONPATH=\${PWD}:\$PYTHONPATH # 运行完整测试套件 pytest tests/ \\ --junitxml=${TEST_REPORTS_DIR}/all-tests.xml \\ --html=${TEST_REPORTS_DIR}/all-tests.html \\ --self-contained-html \\ --cov=entity \\ --cov=services \\ --cov-report=xml:${COVERAGE_DIR}/coverage.xml \\ --cov-report=term-missing \\ --cov-branch \\ --cov-fail-under=70 \\ -v """ } } post { always { // 发布完整测试结果 junit "${TEST_REPORTS_DIR}/all-tests.xml" // 发布代码覆盖率报告 publishCoverage([ adapters: [ [ mergeToOneReport: true, path: "${COVERAGE_DIR}/coverage.xml" ] ], sourceFileResolver: [ [ level: 'NEVER_STORE' ] ] ]) // 发布完整测试HTML报告 publishHTML([ allowMissing: false, alwaysLinkToLastBuild: true, keepAll: true, reportDir: TEST_REPORTS_DIR, reportFiles: 'all-tests.html', reportName: 'Complete Test Report' ]) } } } stage('Performance Tests') { when { // 只在主分支或发布分支运行性能测试 anyOf { branch 'master' branch 'main' branch 'release/*' } } steps { script { echo 'Running performance tests...' sh """ . ${VENV_NAME}/bin/activate export PYTHONPATH=\${PWD}:\$PYTHONPATH # 运行性能测试 RUN_STRESS_TESTS=1 pytest tests/test_integration/test_ffmpeg_execution.py::TestFFmpegExecution::test_stress_test_large_effects_chain \\ --benchmark-json=${TEST_REPORTS_DIR}/benchmark.json \\ --html=${TEST_REPORTS_DIR}/performance-tests.html \\ --self-contained-html \\ -v \\ -m "stress" || true """ } } post { always { // 发布性能测试报告 publishHTML([ allowMissing: true, alwaysLinkToLastBuild: true, keepAll: true, reportDir: TEST_REPORTS_DIR, reportFiles: 'performance-tests.html', reportName: 'Performance Tests Report' ]) } } } } post { always { // 清理虚拟环境 sh """ rm -rf ${VENV_NAME} """ // 归档测试报告 archiveArtifacts artifacts: "${TEST_REPORTS_DIR}/**/*", allowEmptyArchive: true archiveArtifacts artifacts: "${COVERAGE_DIR}/**/*", allowEmptyArchive: true } success { echo 'All tests passed successfully!' // 发送成功通知 script { if (env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'main') { // 这里可以添加Slack、邮件或其他通知 echo 'Sending success notification...' } } } failure { echo 'Tests failed!' // 发送失败通知 script { // 这里可以添加Slack、邮件或其他通知 echo 'Sending failure notification...' } } unstable { echo 'Tests completed with warnings!' } cleanup { // 清理临时文件 deleteDir() } } }