diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7617371e..28082f1a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,11 +1,13 @@ name: using mpm to install MATLAB -on: push +on: [push, pull_request] + jobs: run-tests-job: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] name: Run System tests + if: ${{ github.event_name == 'push' }} runs-on: ${{ matrix.os }} steps: - name: Check out repository @@ -26,4 +28,92 @@ jobs: run: mvn --batch-mode verify env: MATLAB_ROOT: ${{ steps.setup-matlab.outputs.matlabroot }} - MLM_LICENSE_TOKEN: ${{ secrets.MLM_LICENSE_TOKEN}} \ No newline at end of file + MLM_LICENSE_TOKEN: ${{ secrets.MLM_LICENSE_TOKEN}} + + cross-platform-tests: + name: Run Cross Platform Tests + if: ${{ github.event_name == 'pull_request' }} + runs-on: self-hosted + defaults: + run: + shell: powershell + env: + JENKINS_URL: "http://localhost:8080" + JENKINS_USER: "adutt" + JENKINS_TOKEN: ${{ secrets.JENKINS_TOKEN }} + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Build Plugin + run: mvn package -DskipTests + + - name: Move Plugin to Machine + run: | + Write-Host "Downloading Jenkins CLI..." + Invoke-WebRequest -Uri "$env:JENKINS_URL/jnlpJars/jenkins-cli.jar" -OutFile "jenkins-cli.jar" + + $absPath = Convert-Path "target\matlab.hpi" + $fileUri = "file:///$($absPath.Replace('\', '/'))" + + Write-Host "Installing plugin from: $fileUri" + + $installCmd = "java -jar jenkins-cli.jar -s $env:JENKINS_URL -auth $env:JENKINS_USER`:$env:JENKINS_TOKEN install-plugin $fileUri" + cmd /c $installCmd + + Write-Host "Plugin staged." + exit 0 + + - name: Restart Jenkins Server + run: | + Write-Host "Restarting Jenkins Server..." + try { + java -jar jenkins-cli.jar -s $env:JENKINS_URL -auth "$($env:JENKINS_USER):$($env:JENKINS_TOKEN)" restart + } catch { + Write-Host "Restart command sent." + } + + Write-Host "Waiting for Jenkins to restart..." + Start-Sleep -Seconds 10 + $count = 0 + do { + Start-Sleep -Seconds 5 + $count++ + try { + $response = Invoke-WebRequest -Uri "$env:JENKINS_URL/login" -UseBasicParsing -ErrorAction Stop + if ($response.StatusCode -eq 200) { break } + } catch {} + + if ($count -gt 60) { + Write-Error "Timeout waiting for Jenkins to restart." + exit 1 + } + } while ($true) + Write-Host "Jenkins Up." + + - name: Set up Python 3 + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + pip install python-jenkins + + - name: Run cross platform tests + run: | + Write-Host "Running verification..." + python src\test\cross-platform-tests\jenkins_verifier.py ` + --url $env:JENKINS_URL ` + --user $env:JENKINS_USER ` + --token $env:JENKINS_TOKEN ` + --job "test_project" \ No newline at end of file diff --git a/src/test/cross-platform-tests/jenkins_verifier.py b/src/test/cross-platform-tests/jenkins_verifier.py new file mode 100644 index 00000000..c69c742d --- /dev/null +++ b/src/test/cross-platform-tests/jenkins_verifier.py @@ -0,0 +1,169 @@ +import jenkins +import time +import argparse +import sys +import requests + +TEST_CONFIG = { + "ARTIFACT_NAME": "junittestresults.xml", + "EXPECTED_CONTENT": "testAddition", + "ARTIFACT_PATH": "matlabTestArtifacts/junittestresults.xml" +} + + +def trigger_build(server, job_name): + print(f"1. [ACTION] Triggering build for job: {job_name}") + queue_id = server.build_job(job_name) + print(f" -> Job queued. Queue ID: {queue_id}") + return queue_id + +def get_build_number(server, queue_id): + print(" -> Waiting for build to generate a build number...") + while True: + try: + queue_item = server.get_queue_item(queue_id) + if 'executable' in queue_item: + build_number = queue_item['executable']['number'] + print(f" -> Build started. Build Number: {build_number}") + return build_number + if queue_item.get('cancelled'): + print(" -> [ERROR] Build was cancelled in the queue.") + sys.exit(1) + except Exception as e: + print(f" -> Error checking queue: {e}") + time.sleep(2) + +def wait_for_completion(server, job_name, build_number): + print(f"2. [ACTION] Waiting for Build #{build_number} to complete...") + while True: + try: + build_info = server.get_build_info(job_name, build_number) + if not build_info['building']: + result = build_info['result'] + print(f" -> Build finished. Result: {result}") + return result + except Exception as e: + print(f" -> Polling error: {e}") + time.sleep(5) + +def verify_artifact_exists(server, job_name, build_number, filename): + print(f"\n3. [TEST] Artifact Generation Check") + print(f" -> Looking for artifact: '{filename}'") + try: + build_info = server.get_build_info(job_name, build_number) + artifacts = build_info.get('artifacts', []) + + if not artifacts: + print(" -> [DEBUG] Jenkins reports 0 artifacts archived.") + else: + print(f" -> [DEBUG] Found {len(artifacts)} artifact(s):") + for a in artifacts: + print(f" - FileName: '{a['fileName']}' | RelativePath: '{a['relativePath']}'") + + for artifact in artifacts: + if filename in artifact['fileName'] or filename in artifact['relativePath']: + print(f" -> [PASS] Artifact '{filename}' was generated successfully.") + return artifact + + print(f" -> [FAIL] Artifact '{filename}' NOT found in build.") + return None + except Exception as e: + print(f" -> [ERROR] Failed to fetch artifacts: {e}") + return None + +def verify_artifact_content(base_url, auth, job_name, build_number, artifact_path, expected_text): + print(f"\n4. [TEST] Artifact Content Access Check") + print(f" -> Downloading artifact to verify content...") + + base_url = base_url.rstrip('/') + + artifact_url = f"{base_url}/job/{job_name}/{build_number}/artifact/{artifact_path}" + print(f" -> Fetching URL: {artifact_url}") + + try: + response = requests.get(artifact_url, auth=auth) + + if response.status_code != 200: + print(f" -> [FAIL] HTTP Error {response.status_code}. Response: {response.text[:100]}") + return False + + text_content = response.text + + if expected_text in text_content: + print(f" -> Found expected text: '{expected_text}'") + print(f" -> [PASS] Content verification successful.") + return True + else: + print(f" -> [FAIL] Text '{expected_text}' NOT found.") + print(f" -> Actual Content Snippet: {text_content[:200]}...") + return False + except Exception as e: + print(f" -> [ERROR] Failed to download artifact: {e}") + return False + + +def main(): + parser = argparse.ArgumentParser(description="Jenkins Plugin Integration Test Suite") + parser.add_argument("--url", help="Jenkins Server URL") + parser.add_argument("--user", help="Jenkins User ID") + parser.add_argument("--token", help="Jenkins API Token") + parser.add_argument("--job", required=True, help="Job Name") + + args = parser.parse_args() + + jenkins_url = args.url or os.environ.get('JENKINS_URL') + jenkins_user = args.user or os.environ.get('JENKINS_USER') + jenkins_token = args.token or os.environ.get('JENKINS_TOKEN') + + if not all([jenkins_url, jenkins_user, jenkins_token]): + print("Error: Missing connection details (URL, User, or Token).") + sys.exit(1) + + try: + server = jenkins.Jenkins(jenkins_url, username=jenkins_user, password=jenkins_token) + user = server.get_whoami() + print(f"Connected to Jenkins as {user['fullName']}") + print("=========================================") + print("STARTING SEQUENTIAL INTEGRATION TESTS") + print("=========================================") + + queue_id = trigger_build(server, args.job) + build_number = get_build_number(server, queue_id) + result = wait_for_completion(server, args.job, build_number) + + if result != 'SUCCESS': + print(f"\n[CRITICAL FAIL] Build failed with status {result}. Stopping tests.") + sys.exit(1) + else: + print(f" -> [PASS] Basic Build Success") + + artifact_obj = verify_artifact_exists(server, args.job, build_number, TEST_CONFIG["ARTIFACT_NAME"]) + + if not artifact_obj: + print("\n[CRITICAL FAIL] Artifact generation failed. Stopping tests.") + sys.exit(1) + + content_ok = verify_artifact_content( + jenkins_url, + (jenkins_user, jenkins_token), + args.job, + build_number, + artifact_obj['relativePath'], + TEST_CONFIG["EXPECTED_CONTENT"] + ) + + if not content_ok: + print("\n[CRITICAL FAIL] Artifact content is incorrect.") + sys.exit(1) + + print("\n=========================================") + print("ALL TESTS PASSED SUCCESSFULLY") + print("=========================================") + sys.exit(0) + + except Exception as e: + print(f"An unexpected error occurred: {e}") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file