btleplug 0.12.0

A Cross-Platform Rust Bluetooth Low Energy (BLE) GATT library.
Documentation
#
# Run each integration test individually to avoid multiple simultaneous
# BLE connections to the same test peripheral.
#
# Each test_*.rs file under tests/ is its own binary with a single test,
# ensuring process isolation for BLE stack stability.
#
# Usage:
#   .\scripts\run-integration-tests.ps1                    # run all tests
#   .\scripts\run-integration-tests.ps1 test_read_*        # run tests matching a glob
#
# Environment:
#   BTLEPLUG_TEST_PERIPHERAL  - peripheral name (default: btleplug-test)
#   RUST_LOG                  - log level (e.g. debug, btleplug=trace)
#   DELAY                     - seconds to wait between tests (default: 2)
#   TIMEOUT                   - seconds before a test is killed (default: 20)

[CmdletBinding()]
param(
    [Parameter(Position = 0, ValueFromRemainingArguments)]
    [string[]]$Filter
)

$ErrorActionPreference = 'Stop'

$Delay   = if ($env:DELAY)   { [int]$env:DELAY }   else { 2 }
$Timeout = if ($env:TIMEOUT) { [int]$env:TIMEOUT } else { 20 }
$Passed  = 0
$Failed  = 0
$Failures = @()

# Discover test files.
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$TestsDir  = Join-Path (Split-Path -Parent $ScriptDir) 'tests'

$TestNames = Get-ChildItem -Path $TestsDir -Filter 'test_*.rs' -Name |
    ForEach-Object { $_ -replace '\.rs$', '' } |
    Sort-Object

# Apply filter if provided.
if ($Filter) {
    $TestNames = $TestNames | Where-Object {
        $name = $_
        ($Filter | Where-Object { $name -like $_ }).Count -gt 0
    }
}

if ($TestNames.Count -eq 0) {
    Write-Host "No tests matched."
    if ($Filter) { Write-Host "Filter: $($Filter -join ', ')" }
    exit 1
}

$Total = $TestNames.Count
Write-Host "=== btleplug integration tests ==="
Write-Host "Running $Total tests sequentially (${Delay}s delay, ${Timeout}s timeout per test)"
Write-Host ""

$LogFile = Join-Path $env:TEMP 'btleplug-test-output.log'
$TestNum = 0

foreach ($testName in $TestNames) {
    $TestNum++
    Write-Host -NoNewline ("[{0,2}/{1,2}] {2,-55} " -f $TestNum, $Total, $testName)

    # Run cargo test with a timeout.
    # Use [System.Diagnostics.Process] directly: Start-Process -PassThru opens
    # the handle without PROCESS_QUERY_INFORMATION, making ExitCode unavailable.
    $psi = [System.Diagnostics.ProcessStartInfo]@{
        FileName               = 'cargo'
        Arguments              = "test --test $testName -- --ignored"
        UseShellExecute        = $false
        RedirectStandardOutput = $true
        RedirectStandardError  = $true
        WorkingDirectory       = (Get-Location).Path
    }
    $proc = [System.Diagnostics.Process]::new()
    $proc.StartInfo = $psi
    $proc.Start() | Out-Null

    # Drain both pipes concurrently to prevent deadlock if buffers fill.
    $stdoutTask = $proc.StandardOutput.ReadToEndAsync()
    $stderrTask = $proc.StandardError.ReadToEndAsync()

    $finished = $proc.WaitForExit($Timeout * 1000)
    if (-not $finished) {
        try { $proc.Kill($true) } catch {}
    }
    # No-arg WaitForExit ensures the process and its readers are fully done.
    $proc.WaitForExit()
    [System.Threading.Tasks.Task]::WaitAll($stdoutTask, $stderrTask)

    # Persist captured output for potential display below.
    [System.IO.File]::WriteAllText("$LogFile.stdout", $stdoutTask.Result)
    [System.IO.File]::WriteAllText($LogFile, $stderrTask.Result)

    $showOutput = $false
    if (-not $finished) {
        Write-Host "TIMEOUT (${Timeout}s)"
        $Failed++
        $Failures += $testName
        $showOutput = $true
    } elseif ($proc.ExitCode -ne 0) {
        Write-Host "FAIL"
        $Failed++
        $Failures += $testName
        $showOutput = $true
    } else {
        Write-Host "PASS"
        $Passed++
    }

    if ($showOutput) {
        Write-Host "  --- output ---"
        if (Test-Path "$LogFile.stdout") {
            Get-Content "$LogFile.stdout" -Tail 20 | ForEach-Object { "  $_" }
        }
        if (Test-Path $LogFile) {
            Get-Content $LogFile -Tail 20 | ForEach-Object { "  $_" }
        }
        Write-Host "  --- end ---"
    }

    # Brief delay to let the BLE stack settle between tests.
    if ($TestNum -lt $Total) {
        Start-Sleep -Seconds $Delay
    }
}

Remove-Item -Path $LogFile, "$LogFile.stdout" -ErrorAction SilentlyContinue

Write-Host ""
Write-Host "=== Results ==="
Write-Host "  Passed:  $Passed"
Write-Host "  Failed:  $Failed"
Write-Host "  Total:   $Total"

if ($Failures.Count -gt 0) {
    Write-Host ""
    Write-Host "Failed tests:"
    foreach ($f in $Failures) {
        Write-Host "  - $f"
    }
    exit 1
}

Write-Host ""
Write-Host "All tests passed."