Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 96 additions & 2 deletions scripts/tests/setup-dev.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ Describe 'setup-dev uv bootstrap' -Tag 'Unit' {
function uv {
'uv 0.11.21'
}

if ($null -eq (Get-Command chmod -ErrorAction SilentlyContinue)) {
function chmod {
$global:LASTEXITCODE = 0
}
}
}

BeforeEach {
Expand Down Expand Up @@ -137,9 +143,12 @@ Describe 'setup-dev uv bootstrap' -Tag 'Unit' {

It 'Restricts the installer directory before downloading' {
$script:InstallerMode = $null
Mock chmod {
$script:InstallerMode = @($args)
$global:LASTEXITCODE = 0
}
Mock Invoke-VerifiedDownload {
$script:InstallerDirectory = $DestinationDirectory
$script:InstallerMode = [System.IO.File]::GetUnixFileMode($DestinationDirectory)
$path = Join-Path $DestinationDirectory 'verified.ps1'
'$global:LASTEXITCODE = 0' | Set-Content -LiteralPath $path
[pscustomobject]@{ Path = $path }
Expand All @@ -150,7 +159,7 @@ Describe 'setup-dev uv bootstrap' -Tag 'Unit' {
-ExpectedHash ('a' * 64) `
-IsWindowsPlatform $false

[int]$script:InstallerMode | Should -Be 448
$script:InstallerMode | Should -Be @('700', $script:InstallerDirectory)
}

It 'Adds the POSIX uv installation directories before command validation' {
Expand Down Expand Up @@ -253,3 +262,88 @@ Describe 'setup-dev uv bootstrap' -Tag 'Unit' {
} | Should -Throw 'Failed to install uv v0.11.21'
}
}

$script:GitBashPath = if ($env:ProgramFiles) {
Join-Path $env:ProgramFiles 'Git/bin/bash.exe'
}
else {
$null
}
$script:BashAvailable = ($script:GitBashPath -and (Test-Path -LiteralPath $script:GitBashPath)) -or
$null -ne (Get-Command bash -ErrorAction SilentlyContinue)

Describe 'Bash launcher accessibility contracts' -Tag 'Unit' {
BeforeAll {
$script:RepositoryRoot = Resolve-Path (Join-Path $PSScriptRoot '../..')
$gitBash = if ($env:ProgramFiles) {
Join-Path $env:ProgramFiles 'Git/bin/bash.exe'
}
else {
$null
}
$script:BashExecutable = if ($gitBash -and (Test-Path -LiteralPath $gitBash)) {
$gitBash
}
else {
'bash'
}
function ConvertTo-BashPath {
param([string]$Path)

$normalized = $Path.Replace('\', '/')
if ($script:BashExecutable -ne 'bash') {
return $normalized
}
if ($normalized -match '^([A-Za-z]):/(.*)$') {
return "/mnt/$($Matches[1].ToLowerInvariant())/$($Matches[2])"
}
return $normalized
}

$script:SetupDevBashPath = ConvertTo-BashPath (Join-Path $script:RepositoryRoot 'setup-dev.sh')
$script:ViewerStartPath = ConvertTo-BashPath (
Join-Path $script:RepositoryRoot 'data-management/viewer/start.sh'
)
}

It 'Provides setup help without requiring deployment tools' -Skip:(-not $script:BashAvailable) {
$output = & $script:BashExecutable $script:SetupDevBashPath --help 2>&1

$LASTEXITCODE | Should -Be 0
$output -join "`n" | Should -Match 'Usage:'
$output -join "`n" | Should -Match '--config-preview'
}

It 'Previews setup configuration without mutation' -Skip:(-not $script:BashAvailable) {
$output = & $script:BashExecutable $script:SetupDevBashPath --config-preview 2>&1

$LASTEXITCODE | Should -Be 0
$output -join "`n" | Should -Match 'Configuration Preview'
$output -join "`n" | Should -Match 'Mutation.*None'
}

It 'Previews Viewer configuration without ANSI output when NO_COLOR is present' -Skip:(-not $script:BashAvailable) {
$originalNoColor = $env:NO_COLOR
try {
$env:NO_COLOR = '1'
$output = & $script:BashExecutable $script:ViewerStartPath --config-preview 2>&1
}
finally {
$env:NO_COLOR = $originalNoColor
}

$LASTEXITCODE | Should -Be 0
$text = $output -join "`n"
$text | Should -Match 'Configuration Preview'
$text | Should -Match 'Mutation.*None'
$text | Should -Not -Match ([regex]::Escape([char]27))
}

It 'Rejects an unknown Viewer option with visible recovery guidance' -Skip:(-not $script:BashAvailable) {
$output = & $script:BashExecutable $script:ViewerStartPath --not-a-real-option 2>&1

$LASTEXITCODE | Should -Not -Be 0
$output -join "`n" | Should -Match 'Unknown option.*--not-a-real-option'
$output -join "`n" | Should -Match 'Usage:'
}
}
32 changes: 32 additions & 0 deletions setup-dev.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,38 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VENV_DIR="${SCRIPT_DIR}/.venv"
DISABLE_VENV=false
CONFIG_PREVIEW=false

show_help() {
cat << EOF
Usage: $(basename "$0") [OPTIONS]

Prepare the local Physical AI Toolchain development environment.

Options:
--disable-venv Install packages without creating .venv
--config-preview Print local configuration and exit without changes
--help, -h Show this help message
EOF
}

while [[ $# -gt 0 ]]; do
case $1 in
--disable-venv)
DISABLE_VENV=true
shift
;;
--config-preview)
CONFIG_PREVIEW=true
shift
;;
--help|-h)
show_help
exit 0
;;
*)
echo "Unknown option: $1" >&2
show_help >&2
exit 1
;;
esac
Expand All @@ -21,6 +44,15 @@ done
# shellcheck source=scripts/lib/common.sh
source "${SCRIPT_DIR}/scripts/lib/common.sh"

if [[ "${CONFIG_PREVIEW}" == "true" ]]; then
section "Configuration Preview"
print_kv "Repository" "${SCRIPT_DIR}"
print_kv "Python" "$(cat "${SCRIPT_DIR}/.python-version")"
print_kv "Virtual Environment" "$([[ "${DISABLE_VENV}" == "true" ]] && echo disabled || echo "${VENV_DIR}")"
print_kv "Mutation" "None"
exit 0
fi

# Preamble: Recommend devcontainer for easier setup
echo
echo "💡 RECOMMENDED: Use the Dev Container for the best experience."
Expand Down
13 changes: 13 additions & 0 deletions training/tests/test_stream_hypothesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,19 @@ def test_install_ansi_stripping_preserves_existing_tqdm_interval(monkeypatch) ->
assert stream_module.os.environ["TQDM_MININTERVAL"] == "5"


def test_redirected_progress_is_line_oriented_and_rate_limited(monkeypatch) -> None:
"""Redirected progress remains readable without flooding log consumers."""
stdout = CaptureStream()
monkeypatch.setattr(stream_module.sys, "stdout", stdout)
monkeypatch.delenv("TQDM_MININTERVAL", raising=False)

stream_module.install_ansi_stripping()
stream_module.sys.stdout.write("\x1b[32mstep 1\x1b[0m\rstep 2\r")

assert stdout.value == "step 1\nstep 2\n"
assert stream_module.os.environ["TQDM_MININTERVAL"] == "30"


def test_install_ansi_stripping_does_not_double_wrap_stdout(monkeypatch) -> None:
"""install_ansi_stripping keeps an existing AnsiStrippingStream instance."""
existing = AnsiStrippingStream(CaptureStream())
Expand Down
Loading