Test Scripts - Mac, Linux and Windows #118
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Test Scripts - Mac, Linux and Windows | |
| permissions: | |
| contents: read | |
| on: | |
| schedule: | |
| # Run daily at 2 AM UTC | |
| - cron: '0 2 * * *' | |
| pull_request: | |
| branches: | |
| - main | |
| workflow_dispatch: | |
| jobs: | |
| test-mac: | |
| name: Test mac/run.sh on macOS | |
| if: false | |
| runs-on: macos-latest | |
| timeout-minutes: 15 | |
| environment: BrowserStack | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Set up Bash | |
| run: | | |
| echo "Bash version:" | |
| bash --version | |
| - name: Validate shell script syntax | |
| run: | | |
| echo "Validating mac/run.sh syntax..." | |
| bash -n mac/run.sh | |
| echo "✅ mac/run.sh syntax is valid" | |
| - name: Validate supporting scripts syntax | |
| run: | | |
| echo "Validating supporting scripts..." | |
| bash -n mac/common-utils.sh | |
| bash -n mac/logging-utils.sh | |
| bash -n mac/env-setup-run.sh | |
| bash -n mac/user-interaction.sh | |
| bash -n mac/env-prequisite-checks.sh | |
| echo "✅ All supporting scripts are valid" | |
| - name: Check if scripts are executable | |
| run: | | |
| chmod +x mac/run.sh | |
| chmod +x mac/common-utils.sh | |
| chmod +x mac/logging-utils.sh | |
| chmod +x mac/env-setup-run.sh | |
| chmod +x mac/user-interaction.sh | |
| chmod +x mac/env-prequisite-checks.sh | |
| echo "✅ All scripts are executable" | |
| - name: Run ShellCheck on mac scripts | |
| run: | | |
| brew install shellcheck | |
| echo "Running ShellCheck on mac scripts..." | |
| shellcheck -x mac/run.sh || true | |
| shellcheck -x mac/common-utils.sh || true | |
| shellcheck -x mac/logging-utils.sh || true | |
| shellcheck -x mac/env-setup-run.sh || true | |
| shellcheck -x mac/user-interaction.sh || true | |
| shellcheck -x mac/env-prequisite-checks.sh || true | |
| echo "✅ ShellCheck analysis complete" | |
| - name: Verify required dependencies | |
| run: | | |
| echo "Checking required dependencies..." | |
| command -v bash && echo "✅ bash found" | |
| command -v curl && echo "✅ curl found" | |
| command -v git && echo "✅ git found" | |
| command -v bc && echo "✅ bc found" | |
| echo "All required dependencies are available" | |
| - name: Test script sourcing (dry run) | |
| run: | | |
| set -e | |
| echo "Testing script sourcing..." | |
| bash -c "source mac/common-utils.sh && echo '✅ common-utils.sh sourced successfully'" | |
| echo "✅ Script sourcing successful" | |
| - name: Integration Test - Silent Mode Execution | |
| if: success() | |
| env: | |
| BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} | |
| BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} | |
| TURL: https://bstackdemo.com | |
| run: | | |
| echo "Running integration tests in silent mode..." | |
| # Set default values if secrets are not provided | |
| BROWSERSTACK_USERNAME="${BROWSERSTACK_USERNAME:-test_user}" | |
| BROWSERSTACK_ACCESS_KEY="${BROWSERSTACK_ACCESS_KEY:-test_key}" | |
| export BROWSERSTACK_USERNAME | |
| export BROWSERSTACK_ACCESS_KEY | |
| export TURL | |
| # Test configurations | |
| test_configs=( | |
| "web java" | |
| "app java" | |
| "web python" | |
| "app python" | |
| "web nodejs" | |
| "app nodejs" | |
| ) | |
| for config in "${test_configs[@]}"; do | |
| read -r test_type tech_stack <<< "$config" | |
| echo "================================" | |
| echo "Testing: mac/run.sh --silent $test_type $tech_stack" | |
| echo "================================" | |
| # Run with timeout and capture exit code (macOS compatible) | |
| bash mac/run.sh --silent "$test_type" "$tech_stack" >> "/tmp/run_test_${test_type}_${tech_stack}.log" 2>&1 & | |
| job_pid=$! | |
| # Wait with 600 second timeout using sleep loop (macOS compatible) | |
| count=0 | |
| max_wait=600 | |
| while kill -0 "$job_pid" 2>/dev/null && [ $count -lt $max_wait ]; do | |
| sleep 1 | |
| count=$((count + 1)) | |
| done | |
| # Check if process is still running after timeout | |
| if kill -0 "$job_pid" 2>/dev/null; then | |
| echo "⚠️ mac/run.sh --silent $test_type $tech_stack timed out after 600 seconds" | |
| kill -9 "$job_pid" 2>/dev/null || true | |
| exit_code=124 | |
| else | |
| wait "$job_pid" || exit_code=$? | |
| fi | |
| if [ -z "$exit_code" ] || [ "$exit_code" -eq 0 ] || [ "$exit_code" -eq 124 ]; then | |
| echo "✅ mac/run.sh --silent $test_type $tech_stack completed (exit code: ${exit_code:-0})" | |
| else | |
| echo "⚠️ mac/run.sh --silent $test_type $tech_stack exited with code: $exit_code" | |
| if [ -f "/tmp/run_test_${test_type}_${tech_stack}.log" ]; then | |
| echo "Log output (last 20 lines):" | |
| tail -n 20 "/tmp/run_test_${test_type}_${tech_stack}.log" | |
| fi | |
| fi | |
| unset exit_code | |
| done | |
| echo "✅ All integration tests completed" | |
| - name: Sync BrowserStack logs to workspace | |
| if: always() | |
| run: | | |
| mkdir -p ${{ github.workspace }}/bs-logs | |
| if [ -d ~/.browserstack/NOW/logs ]; then | |
| cp -R ~/.browserstack/NOW/logs/* ${{ github.workspace }}/bs-logs/ || true | |
| else | |
| echo "No logs found in ~/.browserstack/NOW/logs" | |
| fi | |
| - name: Upload BrowserStack Logs as Artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: browserstack-logs-macos | |
| path: | | |
| ${{ github.workspace }}/bs-logs | |
| /tmp/run_test_*.log | |
| retention-days: 30 | |
| if-no-files-found: ignore | |
| test-windows: | |
| name: Test win/run.ps1 on Windows | |
| runs-on: windows-latest | |
| timeout-minutes: 25 | |
| environment: BrowserStack | |
| env: | |
| ACTIONS_STEP_DEBUG: true | |
| defaults: | |
| run: | |
| shell: powershell | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Node.js 20.x | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Check PowerShell version | |
| run: | | |
| $PSVersionTable.PSVersion | |
| Write-Host "✅ PowerShell version check complete" | |
| - name: Enable PowerShell debug logging | |
| run: | | |
| Write-Host "Enabling PowerShell debug and verbose logging..." | |
| $DebugPreference = "Continue" | |
| $VerbosePreference = "Continue" | |
| $ErrorActionPreference = "Continue" | |
| Write-Host "✅ Debug logging enabled" | |
| Write-Debug "Debug logging is now active" | |
| Write-Verbose "Verbose logging is now active" | |
| - name: Validate PowerShell script syntax | |
| run: | | |
| Write-Host "Validating win/run.ps1 syntax..." | |
| $ScriptPath = "win/run.ps1" | |
| $null = [System.Management.Automation.PSParser]::Tokenize((Get-Content -Raw $ScriptPath), [ref]$null) | |
| Write-Host "✅ win/run.ps1 syntax is valid" | |
| - name: Validate supporting PowerShell scripts syntax | |
| run: | | |
| Write-Host "Validating supporting PowerShell scripts..." | |
| $Scripts = @( | |
| "win/common-utils.ps1", | |
| "win/logging-utils.ps1", | |
| "win/env-prequisite-checks.ps1", | |
| "win/user-interaction.ps1", | |
| "win/env-setup-run.ps1", | |
| "win/device-machine-allocation.ps1" | |
| ) | |
| foreach ($Script in $Scripts) { | |
| $null = [System.Management.Automation.PSParser]::Tokenize((Get-Content -Raw $Script), [ref]$null) | |
| Write-Host "✅ $Script syntax is valid" | |
| } | |
| - name: Run PSScriptAnalyzer | |
| run: | | |
| $DebugPreference = "Continue" | |
| $VerbosePreference = "Continue" | |
| Write-Host "Installing PSScriptAnalyzer if needed..." | |
| if (-not (Get-Module -ListAvailable -Name PSScriptAnalyzer)) { | |
| Install-Module -Name PSScriptAnalyzer -Force -SkipPublisherCheck -Scope CurrentUser -Verbose | |
| } | |
| Write-Host "Running PSScriptAnalyzer..." | |
| Invoke-ScriptAnalyzer -Path "win" -Recurse -ReportSummary -ErrorAction Continue -Verbose | |
| Write-Host "✅ PSScriptAnalyzer analysis complete (continuing even if issues are found)" | |
| - name: Check script file encoding | |
| run: | | |
| Write-Host "Checking PowerShell script encoding..." | |
| $ScriptPath = "win/run.ps1" | |
| $bytes = [System.IO.File]::ReadAllBytes($ScriptPath) | |
| $encoding = "Unknown / ASCII / UTF-8 without BOM" | |
| if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) { | |
| $encoding = "UTF-8 with BOM" | |
| } elseif ($bytes.Length -ge 2 -and $bytes[0] -eq 0xFF -and $bytes[1] -eq 0xFE) { | |
| $encoding = "UTF-16 LE" | |
| } elseif ($bytes.Length -ge 2 -and $bytes[0] -eq 0xFE -and $bytes[1] -eq 0xFF) { | |
| $encoding = "UTF-16 BE" | |
| } | |
| Write-Host "Detected encoding (heuristic): $encoding" | |
| Write-Host "✅ Encoding check complete" | |
| - name: Verify required dependencies | |
| run: | | |
| Write-Host "Checking required dependencies..." | |
| if (Get-Command curl.exe -ErrorAction SilentlyContinue) { Write-Host "✅ curl found" } else { Write-Host "⚠️ curl not found" } | |
| if (Get-Command git.exe -ErrorAction SilentlyContinue) { Write-Host "✅ git found" } else { Write-Host "⚠️ git not found" } | |
| Write-Host "✅ PowerShell dependencies verified" | |
| - name: Check initial system resources | |
| run: | | |
| Write-Host "=== Initial System Resources ===" | |
| # Get system memory info | |
| $os = Get-CimInstance Win32_OperatingSystem | |
| $totalMemoryGB = [math]::Round($os.TotalVisibleMemorySize / 1MB, 2) | |
| $freeMemoryGB = [math]::Round($os.FreePhysicalMemory / 1MB, 2) | |
| $usedMemoryGB = [math]::Round(($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / 1MB, 2) | |
| $memoryPercent = [math]::Round(($usedMemoryGB / $totalMemoryGB) * 100, 2) | |
| Write-Host "Total Physical Memory: $totalMemoryGB GB" | |
| Write-Host "Free Physical Memory: $freeMemoryGB GB" | |
| Write-Host "Used Physical Memory: $usedMemoryGB GB ($memoryPercent%)" | |
| # Get CPU info | |
| $cpu = Get-CimInstance Win32_Processor | |
| Write-Host "CPU Name: $($cpu.Name)" | |
| Write-Host "CPU Cores: $($cpu.NumberOfCores)" | |
| Write-Host "CPU Logical Processors: $($cpu.NumberOfLogicalProcessors)" | |
| # Get current CPU usage (average over 3 samples) | |
| Write-Host "Measuring CPU usage..." | |
| $cpuSamples = @() | |
| for ($i = 0; $i -lt 3; $i++) { | |
| $cpuSample = (Get-Counter '\Processor(_Total)\% Processor Time').CounterSamples[0].CookedValue | |
| $cpuSamples += $cpuSample | |
| Start-Sleep -Seconds 1 | |
| } | |
| $avgCpu = [math]::Round(($cpuSamples | Measure-Object -Average).Average, 2) | |
| Write-Host "Current CPU Usage: $avgCpu%" | |
| # Get disk space | |
| $disk = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='C:'" | |
| $freeDiskGB = [math]::Round($disk.FreeSpace / 1GB, 2) | |
| $totalDiskGB = [math]::Round($disk.Size / 1GB, 2) | |
| $usedDiskGB = [math]::Round(($disk.Size - $disk.FreeSpace) / 1GB, 2) | |
| Write-Host "C: Drive Free Space: $freeDiskGB GB / $totalDiskGB GB" | |
| Write-Host "✅ Initial resource check complete" | |
| - name: Integration Test - Silent Mode Execution | |
| if: success() | |
| env: | |
| BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} | |
| BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} | |
| TURL: https://bstackdemo.com | |
| run: | | |
| $DebugPreference = "Continue" | |
| $VerbosePreference = "Continue" | |
| $ErrorActionPreference = "Continue" | |
| Write-Host "Running integration tests in silent mode..." | |
| Write-Debug "Debug logging enabled for integration tests" | |
| Write-Verbose "Verbose logging enabled for integration tests" | |
| # Use defaults if secrets are missing (for local / dry runs) | |
| $BrowserStackUsername = if ($env:BROWSERSTACK_USERNAME) { $env:BROWSERSTACK_USERNAME } else { "test_user" } | |
| $BrowserStackAccessKey = if ($env:BROWSERSTACK_ACCESS_KEY) { $env:BROWSERSTACK_ACCESS_KEY } else { "test_key" } | |
| $TestUrl = $env:TURL | |
| Write-Debug "BrowserStack Username: $BrowserStackUsername" | |
| Write-Verbose "Test URL: $TestUrl" | |
| $env:BROWSERSTACK_USERNAME = $BrowserStackUsername | |
| $env:BROWSERSTACK_ACCESS_KEY = $BrowserStackAccessKey | |
| $env:TURL = $TestUrl | |
| # Absolute path is safer in CI | |
| $scriptPath = Join-Path $env:GITHUB_WORKSPACE "win\run.ps1" | |
| Write-Debug "Script path: $scriptPath" | |
| $testConfigs = @( | |
| @("web", "java"), | |
| @("app", "java"), | |
| @("web", "python"), | |
| @("app", "python"), | |
| @("web", "nodejs"), | |
| @("app", "nodejs") | |
| ) | |
| # Function to get system resources | |
| function Get-SystemResources { | |
| $os = Get-CimInstance Win32_OperatingSystem | |
| $totalMemoryGB = [math]::Round($os.TotalVisibleMemorySize / 1MB, 2) | |
| $freeMemoryGB = [math]::Round($os.FreePhysicalMemory / 1MB, 2) | |
| $usedMemoryGB = [math]::Round(($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / 1MB, 2) | |
| $memoryPercent = [math]::Round(($usedMemoryGB / $totalMemoryGB) * 100, 2) | |
| $cpuSample = (Get-Counter '\Processor(_Total)\% Processor Time' -ErrorAction SilentlyContinue).CounterSamples[0].CookedValue | |
| $cpuUsage = [math]::Round($cpuSample, 2) | |
| # Get PowerShell process memory | |
| $pwshProcess = Get-Process -Id $PID -ErrorAction SilentlyContinue | |
| $pwshMemoryMB = if ($pwshProcess) { [math]::Round($pwshProcess.WorkingSet64 / 1MB, 2) } else { 0 } | |
| return @{ | |
| TotalMemoryGB = $totalMemoryGB | |
| FreeMemoryGB = $freeMemoryGB | |
| UsedMemoryGB = $usedMemoryGB | |
| MemoryPercent = $memoryPercent | |
| CpuUsage = $cpuUsage | |
| PwshMemoryMB = $pwshMemoryMB | |
| } | |
| } | |
| $overallFailed = $false | |
| $logRoot = Join-Path $env:TEMP "now-tests" | |
| $resourceLogPath = Join-Path $logRoot "resource_usage.log" | |
| New-Item -ItemType Directory -Force -Path $logRoot | Out-Null | |
| # Log initial resources | |
| $initialResources = Get-SystemResources | |
| Write-Host "=== Initial Resources Before Tests ===" | |
| Write-Host "Memory: $($initialResources.UsedMemoryGB) GB / $($initialResources.TotalMemoryGB) GB ($($initialResources.MemoryPercent)%)" | |
| Write-Host "CPU: $($initialResources.CpuUsage)%" | |
| Write-Host "PowerShell Memory: $($initialResources.PwshMemoryMB) MB" | |
| "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - INITIAL - Memory: $($initialResources.UsedMemoryGB)GB/$($initialResources.TotalMemoryGB)GB ($($initialResources.MemoryPercent)%), CPU: $($initialResources.CpuUsage)%, Pwsh: $($initialResources.PwshMemoryMB)MB" | Out-File -FilePath $resourceLogPath -Append | |
| foreach ($config in $testConfigs) { | |
| $testType = $config[0] | |
| $techStack = $config[1] | |
| Write-Host "================================" | |
| Write-Host "Testing: $scriptPath --silent $testType $techStack" | |
| Write-Host "================================" | |
| Write-Debug "Test type: $testType, Tech stack: $techStack" | |
| # Get resources before test | |
| $resourcesBefore = Get-SystemResources | |
| Write-Host "--- Resources Before Test ---" | |
| Write-Host "Memory: $($resourcesBefore.UsedMemoryGB) GB / $($resourcesBefore.TotalMemoryGB) GB ($($resourcesBefore.MemoryPercent)%)" | |
| Write-Host "CPU: $($resourcesBefore.CpuUsage)%" | |
| Write-Host "PowerShell Memory: $($resourcesBefore.PwshMemoryMB) MB" | |
| "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - BEFORE [$testType/$techStack] - Memory: $($resourcesBefore.UsedMemoryGB)GB/$($resourcesBefore.TotalMemoryGB)GB ($($resourcesBefore.MemoryPercent)%), CPU: $($resourcesBefore.CpuUsage)%, Pwsh: $($resourcesBefore.PwshMemoryMB)MB" | Out-File -FilePath $resourceLogPath -Append | |
| $logPath = Join-Path $logRoot "run_test_${testType}_${techStack}.log" | |
| Write-Verbose "Log path: $logPath" | |
| # Start background resource monitoring | |
| $monitorJob = Start-Job -ScriptBlock { | |
| param($logFile, $testName) | |
| $endTime = (Get-Date).AddMinutes(10) # Monitor for max 10 minutes | |
| while ((Get-Date) -lt $endTime) { | |
| try { | |
| $os = Get-CimInstance Win32_OperatingSystem | |
| $usedMemoryGB = [math]::Round(($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / 1MB, 2) | |
| $totalMemoryGB = [math]::Round($os.TotalVisibleMemorySize / 1MB, 2) | |
| $memoryPercent = [math]::Round(($usedMemoryGB / $totalMemoryGB) * 100, 2) | |
| $cpuSample = (Get-Counter '\Processor(_Total)\% Processor Time' -ErrorAction SilentlyContinue).CounterSamples[0].CookedValue | |
| $cpuUsage = [math]::Round($cpuSample, 2) | |
| "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - MONITOR [$testName] - Memory: $usedMemoryGB GB/$totalMemoryGB GB ($memoryPercent%), CPU: $cpuUsage%" | Out-File -FilePath $logFile -Append | |
| } catch { | |
| # Ignore errors in monitoring | |
| } | |
| Start-Sleep -Seconds 30 # Sample every 30 seconds | |
| } | |
| } -ArgumentList $resourceLogPath, "${testType}_${techStack}" | |
| Write-Debug "Executing script: $scriptPath --silent $testType $techStack" | |
| $startTime = Get-Date | |
| & $scriptPath --silent $testType $techStack 2>&1 | Tee-Object -FilePath $logPath -Append | |
| $exitCode = $LASTEXITCODE | |
| $endTime = Get-Date | |
| $duration = $endTime - $startTime | |
| Write-Debug "Script exit code: $exitCode" | |
| # Stop monitoring job | |
| Stop-Job -Job $monitorJob -ErrorAction SilentlyContinue | |
| Remove-Job -Job $monitorJob -ErrorAction SilentlyContinue | |
| # Get resources after test | |
| Start-Sleep -Seconds 2 # Brief pause to let system settle | |
| $resourcesAfter = Get-SystemResources | |
| Write-Host "--- Resources After Test ---" | |
| Write-Host "Memory: $($resourcesAfter.UsedMemoryGB) GB / $($resourcesAfter.TotalMemoryGB) GB ($($resourcesAfter.MemoryPercent)%)" | |
| Write-Host "CPU: $($resourcesAfter.CpuUsage)%" | |
| Write-Host "PowerShell Memory: $($resourcesAfter.PwshMemoryMB) MB" | |
| Write-Host "Test Duration: $($duration.TotalSeconds) seconds" | |
| $memoryDelta = [math]::Round($resourcesAfter.UsedMemoryGB - $resourcesBefore.UsedMemoryGB, 2) | |
| $cpuDelta = [math]::Round($resourcesAfter.CpuUsage - $resourcesBefore.CpuUsage, 2) | |
| Write-Host "Memory Change: $memoryDelta GB" | |
| Write-Host "CPU Change: $cpuDelta%" | |
| "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - AFTER [$testType/$techStack] - Memory: $($resourcesAfter.UsedMemoryGB)GB/$($resourcesAfter.TotalMemoryGB)GB ($($resourcesAfter.MemoryPercent)%), CPU: $($resourcesAfter.CpuUsage)%, Pwsh: $($resourcesAfter.PwshMemoryMB)MB, Duration: $([math]::Round($duration.TotalSeconds, 2))s, MemoryDelta: ${memoryDelta}GB, CpuDelta: ${cpuDelta}%" | Out-File -FilePath $resourceLogPath -Append | |
| if ($exitCode -eq 0) { | |
| Write-Host "✅ $testType / $techStack completed (exit code: $exitCode)" | |
| } else { | |
| Write-Host "⚠️ $testType / $techStack exited with code: $exitCode" | |
| $overallFailed = $true | |
| if (Test-Path $logPath) { | |
| Write-Host "Log output (last 20 lines):" | |
| Get-Content -Path $logPath -Tail 20 | |
| } | |
| } | |
| } | |
| if ($overallFailed) { | |
| Write-Error "One or more configurations failed." | |
| exit 1 | |
| } | |
| Write-Host "✅ All integration tests completed successfully" | |
| # Log final resources | |
| $finalResources = Get-SystemResources | |
| Write-Host "=== Final Resources After All Tests ===" | |
| Write-Host "Memory: $($finalResources.UsedMemoryGB) GB / $($finalResources.TotalMemoryGB) GB ($($finalResources.MemoryPercent)%)" | |
| Write-Host "CPU: $($finalResources.CpuUsage)%" | |
| Write-Host "PowerShell Memory: $($finalResources.PwshMemoryMB) MB" | |
| $totalMemoryDelta = [math]::Round($finalResources.UsedMemoryGB - $initialResources.UsedMemoryGB, 2) | |
| $totalCpuDelta = [math]::Round($finalResources.CpuUsage - $initialResources.CpuUsage, 2) | |
| Write-Host "Total Memory Change: $totalMemoryDelta GB" | |
| Write-Host "Total CPU Change: $totalCpuDelta%" | |
| "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - FINAL - Memory: $($finalResources.UsedMemoryGB)GB/$($finalResources.TotalMemoryGB)GB ($($finalResources.MemoryPercent)%), CPU: $($finalResources.CpuUsage)%, Pwsh: $($finalResources.PwshMemoryMB)MB, TotalMemoryDelta: ${totalMemoryDelta}GB, TotalCpuDelta: ${totalCpuDelta}%" | Out-File -FilePath $resourceLogPath -Append | |
| Write-Host "Resource usage log saved to: $resourceLogPath" | |
| - name: Check final system resources | |
| if: always() | |
| run: | | |
| Write-Host "=== Final System Resources ===" | |
| # Get system memory info | |
| $os = Get-CimInstance Win32_OperatingSystem | |
| $totalMemoryGB = [math]::Round($os.TotalVisibleMemorySize / 1MB, 2) | |
| $freeMemoryGB = [math]::Round($os.FreePhysicalMemory / 1MB, 2) | |
| $usedMemoryGB = [math]::Round(($os.TotalVisibleMemorySize - $os.FreePhysicalMemory) / 1MB, 2) | |
| $memoryPercent = [math]::Round(($usedMemoryGB / $totalMemoryGB) * 100, 2) | |
| Write-Host "Total Physical Memory: $totalMemoryGB GB" | |
| Write-Host "Free Physical Memory: $freeMemoryGB GB" | |
| Write-Host "Used Physical Memory: $usedMemoryGB GB ($memoryPercent%)" | |
| # Get current CPU usage | |
| Write-Host "Measuring CPU usage..." | |
| $cpuSamples = @() | |
| for ($i = 0; $i -lt 3; $i++) { | |
| $cpuSample = (Get-Counter '\Processor(_Total)\% Processor Time').CounterSamples[0].CookedValue | |
| $cpuSamples += $cpuSample | |
| Start-Sleep -Seconds 1 | |
| } | |
| $avgCpu = [math]::Round(($cpuSamples | Measure-Object -Average).Average, 2) | |
| Write-Host "Current CPU Usage: $avgCpu%" | |
| # Get top processes by memory | |
| Write-Host "`n=== Top 10 Processes by Memory Usage ===" | |
| Get-Process | Sort-Object WorkingSet64 -Descending | Select-Object -First 10 | Format-Table -Property Name, @{Name="Memory(MB)";Expression={[math]::Round($_.WorkingSet64 / 1MB, 2)}}, CPU, Id -AutoSize | |
| # Get top processes by CPU | |
| Write-Host "=== Top 10 Processes by CPU Usage ===" | |
| Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 | Format-Table -Property Name, CPU, @{Name="Memory(MB)";Expression={[math]::Round($_.WorkingSet64 / 1MB, 2)}}, Id -AutoSize | |
| Write-Host "✅ Final resource check complete" | |
| - name: Sync BrowserStack logs to workspace (Windows) | |
| if: always() | |
| run: | | |
| $DebugPreference = "Continue" | |
| $VerbosePreference = "Continue" | |
| $dest = Join-Path $env:GITHUB_WORKSPACE "bs-logs" | |
| Write-Debug "Destination directory: $dest" | |
| New-Item -ItemType Directory -Force -Path $dest -Verbose | Out-Null | |
| $bsLogPath = Join-Path $env:USERPROFILE ".browserstack\NOW\logs" | |
| $tempLogDir = Join-Path $env:TEMP "now-tests" | |
| Write-Debug "BrowserStack log path: $bsLogPath" | |
| Write-Debug "Temp log directory: $tempLogDir" | |
| if (Test-Path $bsLogPath) { | |
| Write-Host "Copying logs from $bsLogPath" | |
| Write-Verbose "Copying logs from $bsLogPath to $dest" | |
| Copy-Item -Path (Join-Path $bsLogPath "*") -Destination $dest -Recurse -Force -ErrorAction SilentlyContinue -Verbose | |
| } else { | |
| Write-Host "No logs found at $bsLogPath" | |
| Write-Debug "BrowserStack logs directory does not exist" | |
| } | |
| if (Test-Path $tempLogDir) { | |
| Write-Host "Copying integration logs from $tempLogDir" | |
| Write-Verbose "Copying integration logs from $tempLogDir to $dest" | |
| Copy-Item -Path (Join-Path $tempLogDir "*") -Destination $dest -Recurse -Force -ErrorAction SilentlyContinue -Verbose | |
| } else { | |
| Write-Debug "Temp log directory does not exist" | |
| } | |
| # Copy resource usage log if it exists | |
| $resourceLogPath = Join-Path $tempLogDir "resource_usage.log" | |
| if (Test-Path $resourceLogPath) { | |
| Write-Host "Copying resource usage log" | |
| Copy-Item -Path $resourceLogPath -Destination $dest -Force -ErrorAction SilentlyContinue -Verbose | |
| } | |
| - name: Display resource usage summary | |
| if: always() | |
| run: | | |
| $resourceLogPath = Join-Path $env:TEMP "now-tests\resource_usage.log" | |
| if (Test-Path $resourceLogPath) { | |
| Write-Host "=== Resource Usage Summary ===" | |
| Write-Host "`nFull resource usage log:" | |
| Get-Content -Path $resourceLogPath | |
| Write-Host "`n✅ Resource usage summary displayed" | |
| } else { | |
| Write-Host "Resource usage log not found at $resourceLogPath" | |
| } | |
| - name: Upload BrowserStack Logs as Artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: browserstack-logs-windows | |
| path: ${{ github.workspace }}/bs-logs | |
| retention-days: 30 | |
| if-no-files-found: ignore | |
| test-linux: | |
| name: Test mac/run.sh on Linux | |
| if: false | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| environment: BrowserStack | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Set up Bash | |
| run: | | |
| echo "Bash version:" | |
| bash --version | |
| - name: Validate shell script syntax | |
| run: | | |
| echo "Validating mac/run.sh syntax..." | |
| bash -n mac/run.sh | |
| echo "✅ mac/run.sh syntax is valid" | |
| - name: Validate supporting scripts syntax | |
| run: | | |
| echo "Validating supporting scripts..." | |
| bash -n mac/common-utils.sh | |
| bash -n mac/logging-utils.sh | |
| bash -n mac/env-setup-run.sh | |
| bash -n mac/user-interaction.sh | |
| bash -n mac/env-prequisite-checks.sh | |
| echo "✅ All supporting scripts are valid" | |
| - name: Check if scripts are executable | |
| run: | | |
| chmod +x mac/run.sh | |
| chmod +x mac/common-utils.sh | |
| chmod +x mac/logging-utils.sh | |
| chmod +x mac/env-setup-run.sh | |
| chmod +x mac/user-interaction.sh | |
| chmod +x mac/env-prequisite-checks.sh | |
| echo "✅ All scripts are executable" | |
| - name: Install ShellCheck | |
| run: | | |
| echo "Installing ShellCheck..." | |
| sudo apt-get update | |
| sudo apt-get install -y shellcheck | |
| shellcheck --version | |
| - name: Run ShellCheck on mac scripts | |
| run: | | |
| echo "Running ShellCheck on mac scripts..." | |
| shellcheck -x mac/run.sh || true | |
| shellcheck -x mac/common-utils.sh || true | |
| shellcheck -x mac/logging-utils.sh || true | |
| shellcheck -x mac/env-setup-run.sh || true | |
| shellcheck -x mac/user-interaction.sh || true | |
| shellcheck -x mac/env-prequisite-checks.sh || true | |
| echo "✅ ShellCheck analysis complete" | |
| - name: Verify required dependencies | |
| run: | | |
| echo "Checking required dependencies..." | |
| command -v bash && echo "✅ bash found" | |
| command -v curl && echo "✅ curl found" | |
| command -v git && echo "✅ git found" | |
| command -v bc && echo "✅ bc found" | |
| echo "All required dependencies are available" | |
| - name: Test script sourcing (dry run) | |
| run: | | |
| set -e | |
| echo "Testing script sourcing..." | |
| bash -c "source mac/common-utils.sh && echo '✅ common-utils.sh sourced successfully'" | |
| echo "✅ Script sourcing successful" | |
| - name: Integration Test - Silent Mode Execution | |
| if: success() | |
| env: | |
| BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} | |
| BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} | |
| TURL: https://bstackdemo.com | |
| run: | | |
| echo "Running integration tests in silent mode..." | |
| # Set default values if secrets are not provided | |
| BROWSERSTACK_USERNAME="${BROWSERSTACK_USERNAME:-test_user}" | |
| BROWSERSTACK_ACCESS_KEY="${BROWSERSTACK_ACCESS_KEY:-test_key}" | |
| export BROWSERSTACK_USERNAME | |
| export BROWSERSTACK_ACCESS_KEY | |
| export TURL | |
| # Test configurations | |
| test_configs=( | |
| "web java" | |
| "app java" | |
| "web python" | |
| "app python" | |
| "web nodejs" | |
| "app nodejs" | |
| ) | |
| for config in "${test_configs[@]}"; do | |
| read -r test_type tech_stack <<< "$config" | |
| echo "================================" | |
| echo "Testing: mac/run.sh --silent $test_type $tech_stack" | |
| echo "================================" | |
| # Run with timeout and capture exit code (using timeout command on Linux) | |
| timeout 600 bash mac/run.sh --silent "$test_type" "$tech_stack" >> "/tmp/run_test_${test_type}_${tech_stack}.log" 2>&1 & | |
| job_pid=$! | |
| wait "$job_pid" || exit_code=$? | |
| if [ -z "$exit_code" ] || [ "$exit_code" -eq 0 ] || [ "$exit_code" -eq 124 ]; then | |
| echo "✅ mac/run.sh --silent $test_type $tech_stack completed (exit code: ${exit_code:-0})" | |
| else | |
| echo "⚠️ mac/run.sh --silent $test_type $tech_stack exited with code: $exit_code" | |
| if [ -f "/tmp/run_test_${test_type}_${tech_stack}.log" ]; then | |
| echo "Log output (last 20 lines):" | |
| tail -n 20 "/tmp/run_test_${test_type}_${tech_stack}.log" | |
| fi | |
| fi | |
| unset exit_code | |
| done | |
| echo "✅ All integration tests completed" | |
| - name: Sync BrowserStack logs to workspace | |
| if: always() | |
| run: | | |
| mkdir -p ${{ github.workspace }}/bs-logs | |
| if [ -d ~/.browserstack/NOW/logs ]; then | |
| cp -R ~/.browserstack/NOW/logs/* ${{ github.workspace }}/bs-logs/ || true | |
| else | |
| echo "No logs found in ~/.browserstack/NOW/logs" | |
| fi | |
| - name: Upload BrowserStack Logs as Artifacts | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: browserstack-logs-linux | |
| path: | | |
| ${{ github.workspace }}/bs-logs | |
| /tmp/run_test_*.log | |
| retention-days: 30 | |
| if-no-files-found: ignore | |
| test-summary: | |
| name: Test Summary | |
| runs-on: ubuntu-latest | |
| needs: [test-mac, test-linux, test-windows] | |
| if: always() | |
| steps: | |
| - name: Check test results | |
| run: | | |
| echo "=== Test Results Summary ===" | |
| echo "macOS Tests: ${{ needs.test-mac.result }}" | |
| echo "Linux Tests: ${{ needs.test-linux.result }}" | |
| echo "Windows Tests: ${{ needs.test-windows.result }}" | |
| if [ "${{ needs.test-mac.result }}" = "failure" ] || [ "${{ needs.test-linux.result }}" = "failure" ] || [ "${{ needs.test-windows.result }}" = "failure" ]; then | |
| echo "❌ Some tests failed" | |
| exit 1 | |
| fi | |
| echo "✅ All tests passed!" | |
| - name: Notify success | |
| if: success() | |
| run: | | |
| echo "✅ All script validations passed successfully!" | |
| echo "- mac/run.sh and supporting scripts validated on macOS and Linux" | |
| echo "- win/run.ps1 and supporting scripts validated on Windows" |