Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
149 commits
Select commit Hold shift + click to select a range
002d7e3
ci: consolidate Ubuntu workflows
steadytao Aug 15, 2026
642b558
ci: restrict workflow permissions
steadytao Aug 15, 2026
0112e7d
ci: bound workflow runtimes
steadytao Aug 15, 2026
cda66ca
ci: pin workflows
steadytao Oct 8, 2026
bd87ec8
testsuite: stabilise daemon argv rejection
steadytao Oct 8, 2026
56a5eb3
testsuite: stabilise legacy peer symlink times
steadytao Oct 8, 2026
1388021
testsuite: make timestamp fixtures deterministic
steadytao Oct 8, 2026
c07311b
testsuite: wait for observable process state
steadytao Oct 8, 2026
c5231be
testsuite: cover inetd socket detection
steadytao Oct 8, 2026
ab4eb8c
testsuite: cover daemon address families
steadytao Oct 8, 2026
7c60c76
testsuite: cover daemon connection limits
steadytao Oct 8, 2026
05aa763
testsuite: refresh coverage matrix
steadytao Oct 8, 2026
6b2cb46
testsuite: cover ignore-missing-args
steadytao Oct 8, 2026
6bfc061
testsuite: cover old-dirs
steadytao Oct 8, 2026
9147537
testsuite: cover iconv
steadytao Oct 8, 2026
1fd965e
testsuite: cover output and logging controls
steadytao Oct 8, 2026
2550a01
testsuite: cover ignore-errors
steadytao Oct 8, 2026
64d287b
testsuite: narrow stderr routing checks
steadytao Oct 8, 2026
7bcc652
ci: cache prepared virtual machines
steadytao Oct 8, 2026
1fe6e5c
testsuite: rework Python test harness
steadytao Oct 9, 2026
7d3b0e6
docs: refresh testsuite documentation
steadytao Oct 9, 2026
d380112
ci: bound NetBSD VM host setup
steadytao Oct 9, 2026
0098f7d
ci: bound remaining VM host setup
steadytao Oct 9, 2026
7378793
testsuite: read xattrs across platforms
steadytao Oct 9, 2026
adcfc02
testsuite: classify Zstandard thread support
steadytao Oct 9, 2026
7110a80
testsuite: support matrix profiles
steadytao Oct 9, 2026
2296e3f
testsuite: classify platform requirements
steadytao Oct 9, 2026
891bfc5
testsuite: retire expected-skip policy
steadytao Oct 9, 2026
5665b0c
testsuite: profile BSD and Solaris
steadytao Oct 9, 2026
6d58140
ci: enforce BSD and Solaris profiles
steadytao Oct 9, 2026
247b1b7
testsuite: complete capability profiles
steadytao Oct 9, 2026
0a17272
testsuite: support wrapped diagnostic runs
steadytao Oct 9, 2026
9434bd7
ci: profile diagnostic workflows
steadytao Oct 9, 2026
04224f6
ci: fix Valgrind matrix
steadytao Oct 10, 2026
05ddde8
testsuite: classify files-from race skips
steadytao Oct 10, 2026
3108639
ci: preserve root Valgrind evidence
steadytao Oct 10, 2026
cbc0e7d
ci: avoid duplicate ASan TCP tests
steadytao Oct 10, 2026
e6695b4
ci: avoid duplicate TCP tests
steadytao Oct 10, 2026
3fd5f8e
ci: pass Valgrind options safely
steadytao Oct 10, 2026
1a8c824
testsuite: organise tests and helpers
steadytao Oct 10, 2026
fdcb94d
testsuite: stage harness for user namespace test
steadytao Oct 10, 2026
22ce65d
testsuite: split process helpers
steadytao Oct 10, 2026
216f7ff
testsuite: simplify test layout
steadytao Oct 10, 2026
e9e753b
ci: trim workflow commentary
steadytao Oct 10, 2026
5bf0903
docs: refine wording/layout
steadytao Oct 10, 2026
8905f8f
testsuite: fix renamed test symlink targets
steadytao Oct 10, 2026
ad58022
testsuite: compose overlapping capabilities
steadytao Oct 10, 2026
820d725
testsuite: move mutation helpers
steadytao Oct 10, 2026
4f4b915
testsuite: separate daemon configuration
steadytao Oct 10, 2026
a6f0efa
testsuite: classify transfer failures
steadytao Nov 8, 2026
b698585
testsuite: remove exit-status wrapper
steadytao Nov 8, 2026
f706159
testsuite: remove capability override
steadytao Nov 8, 2026
0ce99ef
testsuite: fix long socket paths
steadytao Nov 8, 2026
5e7b80e
testsuite: share native checks
steadytao Nov 8, 2026
940bf1b
ci: use shared checks on Ubuntu
steadytao Nov 8, 2026
650f03a
testsuite: fix permission fixtures
steadytao Nov 8, 2026
38abb15
ci: group workflows
steadytao Nov 8, 2026
465eaa2
ci: flatten platform checks
steadytao Nov 8, 2026
46a112a
testsuite: check privilege profiles
steadytao Nov 8, 2026
3339ae6
testsuite: make search-only checks portable
steadytao Nov 8, 2026
84b8c01
ci: run non-root checks
steadytao Nov 8, 2026
9d2a149
testsuite: check build features
steadytao Nov 9, 2026
c57764b
testsuite: fix BSD checks
steadytao Nov 9, 2026
7bc81f9
ci: expand native coverage
steadytao Nov 9, 2026
8c7e6b9
testsuite: fix ACL tool discovery
steadytao Nov 9, 2026
c2ba77b
ci: expand platform matrices
steadytao Nov 9, 2026
a8cdb2f
ci: probe directory access
steadytao Nov 9, 2026
b1685f5
testsuite, ci: tidy names
steadytao Nov 9, 2026
0afa4e1
ci: fix platform checks
steadytao Nov 9, 2026
e569be7
ci: test native Linux ARM64
steadytao Nov 9, 2026
0c13a7d
testsuite: fix portable Linux checks
steadytao Nov 9, 2026
50688e1
ci: test musl and 32-bit Linux
steadytao Nov 9, 2026
08a6dde
ci: fix hosted Linux checks
steadytao Nov 9, 2026
40fd22e
ci: update actions and pin runners
steadytao Nov 9, 2026
8ed0b31
ci: test installed rsync-ssl
steadytao Nov 9, 2026
01ff5db
ci: clarify analysis checks
steadytao Nov 9, 2026
754a6d3
ci: simplify analysis names
steadytao Nov 9, 2026
9107728
ci: restore diagnostics interface
steadytao Nov 9, 2026
f1580ff
testsuite: separate applicability from support
steadytao Nov 9, 2026
0093fc2
chore: reconcile/rebase
steadytao Nov 9, 2026
0232ad9
testsuite: clarify expected failures
steadytao Nov 9, 2026
b2f71bf
testsuite: fix runtime requirements
steadytao Nov 9, 2026
e061739
ci: use sh for portable steps
steadytao Nov 9, 2026
82850aa
testsuite: remove unused tools
steadytao Nov 9, 2026
980a1ed
testsuite: tidy docs
steadytao Nov 9, 2026
574e57b
testsuite: fix profiles and fixtures
steadytao Nov 10, 2026
7c2e8b8
testsuite: tidy tools and docs
steadytao Nov 10, 2026
11394c7
testsuite: fix platform checks
steadytao Nov 10, 2026
7a7e920
ci: clarify architecture labels
steadytao Nov 10, 2026
cba0f08
testsuite: fix paths and account lookup
steadytao Nov 10, 2026
0250e23
testsuite: fix BSD checks
steadytao Nov 10, 2026
79b002e
ci: simplify workflows
steadytao Nov 10, 2026
9ea7e37
testsuite: organise the harness
steadytao Nov 10, 2026
13648a3
ci: group platform workflows
steadytao Nov 10, 2026
be13340
testsuite: fix wrapped rsync lookup
steadytao Nov 10, 2026
894c05d
testsuite: enforce test applicability
steadytao Nov 10, 2026
1849c80
testsuite: strengthen behavioural coverage
steadytao Nov 10, 2026
84fc297
testsuite: simplify test fixtures
steadytao Nov 10, 2026
b9f0ad2
testsuite: fix platform assumptions
steadytao Nov 11, 2026
20f19da
ci: install Git on NetBSD
steadytao Nov 11, 2026
b20655d
testsuite: qualify historical peers in both roles
steadytao Nov 11, 2026
9b74af6
testsuite: expand historical peers
steadytao Nov 11, 2026
3a5b2af
testsuite: make version role self-test portable
steadytao Nov 11, 2026
8fc459f
ci: run Android Bionic and Termux transfers
steadytao Nov 11, 2026
abe7fac
ci: group X86-64 Bionic and Termux checks
steadytao Nov 11, 2026
00f5fba
testsuite: handle divergent handshake clocks
steadytao Nov 11, 2026
cb0a6d1
ci: normalise Android and macOS checks
steadytao Nov 11, 2026
77512af
ci: group Valgrind checks
steadytao Nov 11, 2026
db38ab0
ci: separate Bionic and Termux checks
steadytao Nov 11, 2026
9b914a1
ci: normalise workflow checks
steadytao Nov 11, 2026
5d909d4
ci: require Clang 21
steadytao Nov 11, 2026
c95e88d
docs: restore released NEWS
steadytao Nov 11, 2026
1d2eced
ci: Android emulation
steadytao Nov 11, 2026
564e4be
testsuite: fix recipe timeout race
steadytao Nov 11, 2026
f391d29
testsuite: migrate stream error regressions
steadytao Nov 11, 2026
bd55bb6
testsuite: fix regression transports
steadytao Nov 11, 2026
8c7fd04
ci: consolidate Android checks
steadytao Nov 11, 2026
f18ea58
ci: report emulator startup errors
steadytao Nov 11, 2026
9f7a924
testsuite: support out-of-tree fixtures
steadytao Nov 11, 2026
898d0bf
testsuite: support Python 3.7
steadytao Nov 11, 2026
910dc48
testsuite: fix metadata parsing on newer Python
steadytao Nov 11, 2026
1346e0b
ci: install emulator audio library
steadytao Nov 11, 2026
2b1f22d
packaging: share source archive builder
steadytao Nov 11, 2026
defea87
ci: qualify source archives
steadytao Nov 11, 2026
8bbd2e0
ci: validate interop admission policies
steadytao Nov 12, 2026
2ad08e3
ci: add interop isolation probes
steadytao Nov 12, 2026
58a4685
ci: run interop isolation checks
steadytao Nov 12, 2026
9253590
ci: fix Tailscale setup
steadytao Nov 12, 2026
f1dce8e
ci: temporarily isolate network checks
steadytao Nov 12, 2026
51bc9e1
ci: fix interop API requests
steadytao Nov 12, 2026
1e26293
ci: verify worker API denials
steadytao Nov 12, 2026
9383e47
ci: report interop guard failures
steadytao Nov 12, 2026
bda6cbf
ci: wait for artifact downloads
steadytao Nov 12, 2026
891b0f7
ci: restore full matrix
steadytao Nov 12, 2026
a82147e
ci: isolate interoperability credentials
steadytao Nov 12, 2026
2fded51
testsuite: add interoperability fixture
steadytao Nov 12, 2026
692792f
rebase
steadytao Nov 12, 2026
c5d8e6b
testsuite: verify restricted SSH transfers
steadytao Nov 12, 2026
e64ce13
testsuite: avoid copying symlink metadata
steadytao Nov 12, 2026
6d72471
testsuite: report daemon listing failures
steadytao Nov 12, 2026
228a144
testsuite: share connection fixtures
steadytao Nov 12, 2026
9d43bd3
ci: share Cygwin setup
steadytao Nov 12, 2026
ac26c56
ci: run seven-platform transfers
steadytao Nov 12, 2026
23ddb3c
docs: describe interoperability checks
steadytao Nov 12, 2026
33c32f9
ci: use master interoperability controller
steadytao Nov 12, 2026
5de664e
Merge remote-tracking branch 'origin/master' into rework-workflows
steadytao Nov 12, 2026
25e4542
ci: fix interoperability setup
steadytao Nov 12, 2026
8257061
ci: focus on interoperability
steadytao Nov 12, 2026
3c89896
Merge remote-tracking branch 'origin/master' into rework-workflows
steadytao Nov 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
/rsync-web/ export-ignore

# old_versions/ holds static binaries of historical rsync releases, used by the
# version-mixing test suite (.github/workflows/ubuntu-version-mix.yml) to run
# version-mixing test suite (.github/workflows/ci.yml) to run
# the current code against a real old peer over the daemon / remote-shell.
# Mark the binaries as binary so the `text=auto eol=lf` rule above can't try to
# normalise line endings and corrupt them; export-ignore keeps them out of the
# release source tarball.
/old_versions/rsync_* binary
/old_versions/rsync_* export-ignore
/old_versions/binaries.json export-ignore
160 changes: 160 additions & 0 deletions .github/actions/interoperability/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
name: Directed transfers
description: Native SSH and daemon transfers between seven endpoints
inputs:
platform:
required: true
description: Native endpoint
guest:
default: 'false'
description: Use the existing local VM SSH connection
runs:
using: composite
steps:
- name: Install Tailscale
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INTEROP_PLATFORM: ${{ inputs.platform }}
INTEROP_GUEST: ${{ inputs.guest }}
run: python3 -B -S .github/scripts/interop-host.py install "$INTEROP_PLATFORM" --root "$RUNNER_TEMP/interoperability"
- name: Generate enrolment recipient
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INTEROP_PLATFORM: ${{ inputs.platform }}
INTEROP_GUEST: ${{ inputs.guest }}
run: python3 -B -S .github/scripts/interop-host.py recipient "$INTEROP_PLATFORM" --root "$RUNNER_TEMP/interoperability"
- name: Upload recipient
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: interoperability-${{ github.run_attempt }}-public-${{ inputs.platform }}
path: ${{ runner.temp }}/interoperability/public-key.json
if-no-files-found: error
retention-days: 7
- name: Join quarantine
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INTEROP_PLATFORM: ${{ inputs.platform }}
INTEROP_GUEST: ${{ inputs.guest }}
run: python3 -B -S .github/scripts/interop-host.py connect "$INTEROP_PLATFORM" --root "$RUNNER_TEMP/interoperability"
- name: Start native fixture services
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INTEROP_PLATFORM: ${{ inputs.platform }}
INTEROP_GUEST: ${{ inputs.guest }}
run: python3 -B -S .github/scripts/interop-host.py prepare "$INTEROP_PLATFORM" --root "$RUNNER_TEMP/interoperability"
- name: Upload endpoint identity
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: interoperability-${{ github.run_attempt }}-ready-${{ inputs.platform }}
path: ${{ runner.temp }}/interoperability/ready.json
if-no-files-found: error
retention-days: 7
- name: Check quarantine
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INTEROP_PLATFORM: ${{ inputs.platform }}
INTEROP_GUEST: ${{ inputs.guest }}
run: python3 -B -S .github/scripts/interop-host.py quarantine "$INTEROP_PLATFORM" --root "$RUNNER_TEMP/interoperability"
- name: Upload quarantine result
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: interoperability-${{ github.run_attempt }}-quarantine-${{ inputs.platform }}
path: ${{ runner.temp }}/interoperability/quarantine.json
if-no-files-found: error
retention-days: 7
- name: Check admission
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INTEROP_PLATFORM: ${{ inputs.platform }}
INTEROP_GUEST: ${{ inputs.guest }}
run: python3 -B -S .github/scripts/interop-host.py admit "$INTEROP_PLATFORM" --root "$RUNNER_TEMP/interoperability"
- name: Upload endpoint admission
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: interoperability-${{ github.run_attempt }}-admitted-${{ inputs.platform }}
path: ${{ runner.temp }}/interoperability/admitted.json
if-no-files-found: error
retention-days: 7
- name: Initial transfers
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INTEROP_PLATFORM: ${{ inputs.platform }}
INTEROP_GUEST: ${{ inputs.guest }}
run: python3 -B -S .github/scripts/interop-host.py transfer1 "$INTEROP_PLATFORM" --root "$RUNNER_TEMP/interoperability"
- name: Upload initial transfer completion
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: interoperability-${{ github.run_attempt }}-transfer1-${{ inputs.platform }}
path: ${{ runner.temp }}/interoperability/transfer1.json
if-no-files-found: error
retention-days: 7
- name: Verify initial data and update sources
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INTEROP_PLATFORM: ${{ inputs.platform }}
INTEROP_GUEST: ${{ inputs.guest }}
run: python3 -B -S .github/scripts/interop-host.py next "$INTEROP_PLATFORM" --root "$RUNNER_TEMP/interoperability"
- name: Upload update readiness
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: interoperability-${{ github.run_attempt }}-next-${{ inputs.platform }}
path: ${{ runner.temp }}/interoperability/next.json
if-no-files-found: error
retention-days: 7
- name: Checksum updates and deletion
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INTEROP_PLATFORM: ${{ inputs.platform }}
INTEROP_GUEST: ${{ inputs.guest }}
run: python3 -B -S .github/scripts/interop-host.py transfer2 "$INTEROP_PLATFORM" --root "$RUNNER_TEMP/interoperability"
- name: Upload update completion
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: interoperability-${{ github.run_attempt }}-transfer2-${{ inputs.platform }}
path: ${{ runner.temp }}/interoperability/transfer2.json
if-no-files-found: error
retention-days: 7
- name: Verify updated native data
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INTEROP_PLATFORM: ${{ inputs.platform }}
INTEROP_GUEST: ${{ inputs.guest }}
run: python3 -B -S .github/scripts/interop-host.py verify "$INTEROP_PLATFORM" --root "$RUNNER_TEMP/interoperability"
- name: Upload transfer result
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: interoperability-${{ github.run_attempt }}-result-${{ inputs.platform }}
path: ${{ runner.temp }}/interoperability/result.json
if-no-files-found: error
retention-days: 7
- name: Check revocation
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INTEROP_PLATFORM: ${{ inputs.platform }}
INTEROP_GUEST: ${{ inputs.guest }}
run: python3 -B -S .github/scripts/interop-host.py revoked "$INTEROP_PLATFORM" --root "$RUNNER_TEMP/interoperability"
- name: Upload revocation result
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: interoperability-${{ github.run_attempt }}-revoked-${{ inputs.platform }}
path: ${{ runner.temp }}/interoperability/revoked.json
if-no-files-found: error
retention-days: 7
- name: Stop fixture services and leave the network
if: always()
shell: bash
env:
GH_TOKEN: ${{ github.token }}
INTEROP_PLATFORM: ${{ inputs.platform }}
INTEROP_GUEST: ${{ inputs.guest }}
run: python3 -B -S .github/scripts/interop-host.py stop "$INTEROP_PLATFORM" --root "$RUNNER_TEMP/interoperability"
178 changes: 178 additions & 0 deletions .github/scripts/android-emulator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import argparse
from contextlib import redirect_stdout
import hashlib
import json
import os
from pathlib import Path
import re
import shutil
import signal
import subprocess
import sys
import tempfile
import time

from android import check_transfer as transfer, quote_command, run


def wait_for_boot(adb, proc, timeout=300):
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
if proc.poll() is not None:
raise RuntimeError('Emulator exited before boot completed')
try:
if adb('shell', 'getprop sys.boot_completed', capture_output=True, timeout=10).stdout.strip() == '1':
return
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
pass
time.sleep(2)
raise TimeoutError('Android boot did not complete')


def stop_emulator(adb, proc):
if proc.poll() is not None:
return
try:
adb('emu', 'kill', timeout=10, capture_output=True)
proc.wait(timeout=20)
return
except subprocess.SubprocessError:
proc.terminate()
try:
proc.wait(timeout=10)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait(timeout=10)


def check_device(adb):
values = {}
commands = {'api': ('getprop', 'ro.build.version.sdk'),
'abi': ('getprop', 'ro.product.cpu.abi'),
'emulator': ('getprop', 'ro.kernel.qemu'),
'selinux': ('getenforce',), 'uid': ('id', '-u'),
'kernel': ('uname', '-r'), 'fingerprint': ('getprop', 'ro.build.fingerprint')}
for name, command in commands.items():
values[name] = adb('shell', quote_command(command), capture_output=True).stdout.strip()
for name, expected in dict(api='36', abi='x86_64', emulator='1', selinux='Enforcing', uid='2000').items():
if values[name] != expected:
raise ValueError(f'Android {name}: expected {expected}, got {values[name]!r}')
print(json.dumps(values, indent=2), flush=True)
return values


def check_transfer(adb, binary, output):
device = check_device(adb)
work = adb('shell', 'mktemp -d /data/local/tmp/rsync-ci.XXXXXXXX', capture_output=True).stdout.strip()
if not re.fullmatch(r'/data/local/tmp/rsync-ci\.[A-Za-z0-9]{8}', work):
raise ValueError('Invalid Android scratch directory')
try:
adb('push', str(binary), f'{work}/{binary.name}', timeout=60)
digest = hashlib.sha256(binary.read_bytes()).hexdigest()
remote = adb('shell', quote_command(['sha256sum', f'{work}/{binary.name}']),
capture_output=True).stdout.split()[0]
if digest != remote:
raise ValueError('Android binary hash does not match the candidate')
adb('shell', quote_command(['chmod', '700', f'{work}/{binary.name}']))
def command(*args, **kwargs):
return adb('shell', '-T', quote_command(args), **kwargs)
with tempfile.TemporaryDirectory(prefix='rsync-android-data-') as directory:
local = Path(directory) / 'data'
def write_file(path, data):
local.write_bytes(data)
adb('push', str(local), path, capture_output=True)
def read_file(path):
adb('pull', path, str(local), capture_output=True)
return local.read_bytes()
with (output / 'transfer.log').open('w') as log, redirect_stdout(log):
transfer(command, f'{work}/{binary.name}', work, hardlinks=False,
write_file=write_file, read_file=read_file)
check_permissions(adb, binary, work, output)
receipt = {'device': device, 'binary_sha256': digest,
'source': run('git', 'rev-parse', 'HEAD', capture_output=True).stdout.strip()}
(output / 'receipt.json').write_text(json.dumps(receipt, indent=2) + '\n')
finally:
failed = sys.exc_info()[0] is not None
try:
adb('shell', quote_command(['rm', '-rf', work]))
except (OSError, subprocess.SubprocessError) as error:
if not failed:
raise
print(f'Android scratch cleanup failed: {error}', file=sys.stderr)


def check_permissions(adb, binary, work, output):
with tempfile.TemporaryDirectory(prefix='rsync-unreadable-') as directory:
private = Path(directory) / 'private'
private.write_text('must not be copied\n')
adb('shell', quote_command(['mkdir', f'{work}/unreadable', f'{work}/refused']))
adb('push', str(private), f'{work}/unreadable/private')
adb('shell', quote_command(['chmod', '000', f'{work}/unreadable/private']))
result = adb('shell', quote_command([f'{work}/{binary.name}', '-r', f'{work}/unreadable/',
f'{work}/refused/']), capture_output=True, check=False)
(output / 'permissions.log').write_text(result.stdout + result.stderr)
if result.returncode != 23 or 'Permission denied' not in result.stderr:
raise ValueError('Unreadable Android source did not fail with permission denied and exit 23')
adb('shell', quote_command(['test', '!', '-e', f'{work}/refused/private']))
adb('shell', 'test -d /data/system')
adb('shell', 'test ! -r /data/system')


def main():
parser = argparse.ArgumentParser()
parser.add_argument('--binary', type=Path, default=Path('rsync'))
parser.add_argument('--output', type=Path, default=Path('test-results/android-emulator'))
args = parser.parse_args()
binary = args.binary.resolve(strict=True)
args.output.mkdir(parents=True)
sdk = Path(os.environ['ANDROID_HOME'])
os.environ['PATH'] = os.pathsep.join(str(sdk / path) for path in (
'platform-tools', 'emulator', 'cmdline-tools/latest/bin')) + os.pathsep + os.environ.get('PATH', '')
image = sdk / 'system-images/android-36/google_apis/x86_64/source.properties'
properties = image.read_text()
if 'Pkg.Revision=7' not in properties.splitlines():
raise ValueError('Expected Android API 36 Google APIs X86-64 image revision 7')
(args.output / 'image.properties').write_text(properties)
emulator = shutil.which('emulator')
if not emulator:
raise ValueError('Android emulator is not on PATH')
version = run(emulator, '-version', capture_output=True, check=False)
(args.output / 'emulator-version.log').write_text(version.stdout + version.stderr)
if version.returncode:
raise ValueError(f'Emulator version probe failed ({version.returncode}): {version.stdout}{version.stderr}')
run(emulator, '-accel-check')
serial = 'emulator-5560'
def adb(*command, **kwargs):
return run('adb', '-s', serial, *command, **kwargs)
if serial in run('adb', 'devices', capture_output=True).stdout.split():
raise ValueError(f'{serial} is already in use')
def cancel(signum, frame):
raise SystemExit(128 + signum)
previous = {sig: signal.signal(sig, cancel) for sig in (signal.SIGINT, signal.SIGTERM)}
try:
with tempfile.TemporaryDirectory(prefix='rsync-avd-') as directory:
env = {**os.environ, 'ANDROID_AVD_HOME': directory}
run('avdmanager', 'create', 'avd', '--name', 'rsync', '--package',
'system-images;android-36;google_apis;x86_64', input='no\n', env=env, timeout=60)
with (args.output / 'emulator.log').open('w') as log:
proc = subprocess.Popen([emulator, '-avd', 'rsync', '-port', '5560', '-accel', 'on',
'-no-window', '-no-audio', '-no-boot-anim', '-no-snapshot',
'-gpu', 'swiftshader', '-memory', '2048', '-cores', '2'],
env=env, stdout=log, stderr=subprocess.STDOUT)
try:
wait_for_boot(adb, proc)
check_transfer(adb, binary, args.output)
finally:
stop_emulator(adb, proc)
finally:
for sig, handler in previous.items():
signal.signal(sig, handler)
print((args.output / 'transfer.log').read_text(), end='')


if __name__ == '__main__':
try:
main()
except (OSError, ValueError, RuntimeError, subprocess.SubprocessError) as error:
print(f'Android emulator check failed: {error}', file=sys.stderr)
raise SystemExit(1)
Loading
Loading