Skip to content

Commit 1c8fdce

Browse files
committed
Tests: Add Distiller test variant
Run the test suite against Adobe Acrobat Distiller. run.ps1 executes each test file in its own Distiller invocation and classifies its log; fontalias.ps maps the base-14 font names to the host equivalents; assemble-distiller.sh builds a portable Distiller tree from a licensed, checksum-verified Acrobat archive the user supplies; remote.sh provisions a Windows host and drives the run. No Adobe binaries are included.
1 parent de5d112 commit 1c8fdce

5 files changed

Lines changed: 333 additions & 0 deletions

File tree

tests/distiller_tests/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Distiller test variant
2+
3+
Run the BWIPP test suite against Adobe Acrobat Distiller.
4+
5+
## Requirements
6+
7+
- `acrodist.exe` from an Acrobat install, or a portable tree built by
8+
`assemble-distiller.sh` below. Not included here; supply your own licensed
9+
copy.
10+
- `build/monolithic/barcode.ps` (`make monolithic`).
11+
- Windows with PowerShell.
12+
13+
## Build a portable Distiller
14+
15+
`assemble-distiller.sh` builds a self-contained Distiller tree from an Acrobat
16+
archive you are licensed to use. Requires `7z`, `unshield`, `sha256sum`.
17+
18+
One archive known to work is the Internet Archive item `adobe-acrobat-5.0.1.7z`
19+
(148,758,180 bytes, example SHA-256
20+
`446c06ae42c4d71d0298188ad2587178708b58a0e04545efb4035078ec1b2477`; override
21+
`EXPECT_SHA256` for a different archive):
22+
23+
```sh
24+
curl -fLo 'Adobe Acrobat 5.0.1.7z' \
25+
https://archive.org/download/adobe-acrobat-5.0.1.7z/Adobe%20Acrobat%205.0.1.7z
26+
tests/distiller_tests/assemble-distiller.sh 'Adobe Acrobat 5.0.1.7z' ./out
27+
# -> ./out/Distiller/
28+
```
29+
30+
## Run
31+
32+
`barcode.ps` must be built where the toolchain is (Ghostscript, Perl, make) —
33+
typically not the Windows host — and supplied to the runner.
34+
35+
From a POSIX shell, which builds it and pushes it with the harness, provisions
36+
the host from `DIST_SRC`, runs, and relays the exit code (the host must be
37+
reachable over SSH with PowerShell as its default shell):
38+
39+
```sh
40+
make -j "$(nproc)" monolithic
41+
HOST=user@winhost DIST_SRC=./out/Distiller tests/distiller_tests/remote.sh
42+
```
43+
44+
Or directly on the Windows host, once a built `barcode.ps` has been copied over:
45+
46+
```powershell
47+
pwsh tests/distiller_tests/run.ps1 `
48+
-Distiller C:\path\to\Distiller\acrodist.exe `
49+
-Monolithic C:\path\to\barcode.ps
50+
```
51+
52+
| `run.ps1` parameter | Default | Purpose |
53+
|---------------------|------------------|----------------------------------|
54+
| `-Distiller` | `$env:ACRODIST` | path to `acrodist.exe` |
55+
| `-Monolithic` | repo build path | built `barcode.ps` |
56+
| `-Timeout` | `900` | per-file timeout in seconds |
57+
| `-Skip` | *(empty)* | test names to skip |
58+
| `-Filter` | `*` | restrict to matching test names |
59+
60+
Prints `PASS`/`FAIL`/`SKIP` per file with a summary, and exits non-zero on
61+
failure.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/bin/bash
2+
3+
# Barcode Writer in Pure PostScript
4+
# https://bwipp.terryburton.co.uk
5+
#
6+
# Copyright (c) 2004-2026 Terry Burton
7+
#
8+
#
9+
# Build a portable Adobe Distiller tree from an Acrobat archive you are
10+
# licensed to use, for use with run.ps1. Verifies the archive, extracts its
11+
# InstallShield cabinet, and assembles <output-dir>/Distiller/. Nothing is
12+
# downloaded.
13+
#
14+
# assemble-distiller.sh <Adobe Acrobat 5.0.1.7z> <output-dir>
15+
#
16+
# One archive known to work is the Internet Archive item 'adobe-acrobat-5.0.1.7z'
17+
# (https://archive.org/details/adobe-acrobat-5.0.1.7z). EXPECT_SHA256 below is
18+
# that archive's checksum; override it for a different archive.
19+
#
20+
# Requires: 7z (or 7za), unshield, sha256sum.
21+
22+
set -euo pipefail
23+
24+
ARCHIVE=${1:?usage: assemble-distiller.sh <Adobe Acrobat 5.0.1.7z> <output-dir>}
25+
OUT=${2:?usage: assemble-distiller.sh <Adobe Acrobat 5.0.1.7z> <output-dir>}
26+
27+
# Example SHA-256 for the archive named in the header; override to verify a
28+
# different one.
29+
EXPECT_SHA256=${EXPECT_SHA256:-446c06ae42c4d71d0298188ad2587178708b58a0e04545efb4035078ec1b2477}
30+
31+
sevenzip=$(command -v 7z || command -v 7za || true)
32+
[ -n "$sevenzip" ] || { echo "error: 7z/7za not found (apt install p7zip-full)" >&2; exit 1; }
33+
command -v unshield >/dev/null 2>&1 || { echo "error: unshield not found (apt install unshield)" >&2; exit 1; }
34+
command -v sha256sum >/dev/null 2>&1 || { echo "error: sha256sum not found" >&2; exit 1; }
35+
[ -f "$ARCHIVE" ] || { echo "error: no such archive: $ARCHIVE" >&2; exit 1; }
36+
37+
echo "Verifying $ARCHIVE ..."
38+
got=$(sha256sum "$ARCHIVE" | cut -d' ' -f1)
39+
if [ "$got" != "$EXPECT_SHA256" ]; then
40+
echo "error: checksum mismatch; refusing to proceed" >&2
41+
echo " expected $EXPECT_SHA256" >&2
42+
echo " got $got" >&2
43+
exit 1
44+
fi
45+
46+
TMP=$(mktemp -d)
47+
trap 'rm -rf "$TMP"' EXIT
48+
49+
echo "Extracting installer cabinet ..."
50+
# The InstallShield cabinet (data1.cab + data1.hdr) lives under BS-ACRO5/.
51+
"$sevenzip" x -y -o"$TMP" "$ARCHIVE" -r data1.cab data1.hdr >/dev/null
52+
CAB=$(find "$TMP" -name data1.cab | head -1)
53+
[ -n "$CAB" ] && [ -f "$(dirname "$CAB")/data1.hdr" ] || {
54+
echo "error: data1.cab / data1.hdr not found in archive" >&2; exit 1; }
55+
56+
echo "Assembling Distiller tree ..."
57+
for g in "Distiller Program Files" "Distiller Win NT System files" \
58+
"Distiller Settings English" "Fonts" "PSDisk"; do
59+
unshield -g "$g" -d "$TMP/$g" x "$CAB" >/dev/null || {
60+
echo "error: could not extract group '$g' (unexpected cabinet contents)" >&2; exit 1; }
61+
done
62+
63+
D="$OUT/Distiller"
64+
rm -rf "$D"
65+
mkdir -p "$D/Font" "$D/Data"
66+
67+
# acrodist.exe + DLLs + Data/ (distinit.ps, DISTSADB.DOS, ...); locate by the
68+
# binary so we are robust to how unshield names the destination directory.
69+
pf=$(find "$TMP/Distiller Program Files" -name acrodist.exe -printf '%h\n' | head -1)
70+
[ -n "$pf" ] || { echo "error: acrodist.exe not present in cabinet" >&2; exit 1; }
71+
cp -a "$pf/." "$D/"
72+
73+
find "$TMP/Distiller Win NT System files" -name 'PdfPorts.dll' -exec cp {} "$D/" \;
74+
find "$TMP/Distiller Settings English" -type d -name Settings -exec cp -a {} "$D/" \;
75+
find "$TMP/Distiller Settings English" -type d -name Startup -exec cp -a {} "$D/" \;
76+
find "$TMP/Fonts" -type f -iname '*.pfb' -exec cp {} "$D/Font/" \;
77+
78+
# psdisk resource tree -> Data/psdisk so distinit's relative GenericResourceDir
79+
# (data/psdisk/Resource/) resolves; Windows resolves Data/ case-insensitively.
80+
psd=$(find "$TMP/PSDisk" -type d -name psdisk | head -1)
81+
[ -n "$psd" ] || { echo "error: psdisk resource tree not present in cabinet" >&2; exit 1; }
82+
cp -a "$psd" "$D/Data/"
83+
84+
# Drop the external CMYK profile the job options reference, so Distiller needs
85+
# no colour profile installed on the host (which would require elevation).
86+
for jo in "$D"/Settings/*.joboptions; do
87+
sed -i -e 's#/CalCMYKProfile (.*)#/CalCMYKProfile ()#' \
88+
-e 's#/ColorConversionStrategy /[A-Za-z]*#/ColorConversionStrategy /LeaveColorUnchanged#' "$jo"
89+
done
90+
91+
[ -f "$D/acrodist.exe" ] || { echo "error: assembly incomplete (no acrodist.exe)" >&2; exit 1; }
92+
93+
echo "Assembled portable Distiller at: $D"
94+
echo " dlls=$(ls "$D"/*.dll 2>/dev/null | wc -l) fonts=$(ls "$D/Font" 2>/dev/null | wc -l) psdisk=$([ -d "$D/Data/psdisk/Resource" ] && echo yes || echo no)"
95+
echo
96+
echo "Next: copy $D onto a Windows host, or let remote.sh provision it."

tests/distiller_tests/fontalias.ps

1.46 KB
Binary file not shown.

tests/distiller_tests/remote.sh

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#!/bin/bash
2+
3+
# Barcode Writer in Pure PostScript
4+
# https://bwipp.terryburton.co.uk
5+
#
6+
# Copyright (c) 2004-2026 Terry Burton
7+
#
8+
#
9+
# Run the Distiller test suite on a remote Windows host from a POSIX shell.
10+
# Builds the monolithic locally, pushes it with the harness and test files,
11+
# provisions Distiller when DIST_SRC is given, then invokes run.ps1 remotely
12+
# and relays its exit code. The host must be reachable over SSH with
13+
# PowerShell as the default shell.
14+
#
15+
# HOST user@host (required)
16+
# ACRODIST remote path to acrodist.exe (default: $REMOTE_DIR/Distiller/acrodist.exe)
17+
# DIST_SRC local assembled Distiller dir (optional; provisions the host when set)
18+
# REMOTE_DIR remote staging dir (default: C:/distiller-suite)
19+
# FORCE re-push Distiller even if present (default: unset)
20+
#
21+
# Extra arguments are forwarded to run.ps1 (e.g. -Filter ean13 -Timeout 120).
22+
#
23+
# HOST=tez@host DIST_SRC=./out/Distiller tests/distiller_tests/remote.sh -Filter ean13
24+
25+
set -euo pipefail
26+
27+
HOST=${HOST:?set HOST=user@host}
28+
REMOTE_DIR=${REMOTE_DIR:-C:/distiller-suite}
29+
ACRODIST=${ACRODIST:-$REMOTE_DIR/Distiller/acrodist.exe}
30+
DIST_SRC=${DIST_SRC:-}
31+
FORCE=${FORCE:-}
32+
33+
HERE=$(cd "$(dirname "$0")" && pwd)
34+
ROOT=$(cd "$HERE/../.." && pwd)
35+
MONO="$ROOT/build/monolithic/barcode.ps"
36+
PSDIR="$ROOT/tests/ps_tests"
37+
38+
[ -f "$MONO" ] || { echo "error: missing $MONO (run: make monolithic)" >&2; exit 1; }
39+
40+
# Reachability: skip cleanly rather than fail the caller if the host is down.
41+
if ! ssh -o BatchMode=yes -o ConnectTimeout=10 "$HOST" "exit" >/dev/null 2>&1; then
42+
echo "SKIPPED $HOST not reachable over SSH"
43+
exit 0
44+
fi
45+
46+
psh() { ssh -o BatchMode=yes "$HOST" "powershell -NoProfile -ExecutionPolicy Bypass -Command $1"; }
47+
48+
psh "New-Item -ItemType Directory -Force -Path '$REMOTE_DIR/tests/distiller_tests','$REMOTE_DIR/tests/ps_tests' | Out-Null" >/dev/null
49+
50+
# Provision a portable Distiller when a source is supplied and it is absent
51+
# (or FORCE is set).
52+
if [ -n "$DIST_SRC" ]; then
53+
[ -f "$DIST_SRC/acrodist.exe" ] || { echo "error: $DIST_SRC has no acrodist.exe" >&2; exit 1; }
54+
present=$(psh "Test-Path '$ACRODIST'" | tr -d '\r')
55+
if [ "$present" != "True" ] || [ -n "$FORCE" ]; then
56+
echo "Provisioning Distiller -> $REMOTE_DIR/Distiller"
57+
scp -q -o BatchMode=yes -r "$DIST_SRC" "$HOST:$REMOTE_DIR/"
58+
fi
59+
fi
60+
61+
# Push harness, tests and the freshly built monolithic.
62+
scp -q -o BatchMode=yes "$HERE/run.ps1" "$HERE/fontalias.ps" "$HOST:$REMOTE_DIR/tests/distiller_tests/"
63+
scp -q -o BatchMode=yes "$PSDIR/test_utils.ps" "$PSDIR"/*.ps.test "$HOST:$REMOTE_DIR/tests/ps_tests/"
64+
scp -q -o BatchMode=yes "$MONO" "$HOST:$REMOTE_DIR/barcode.ps"
65+
66+
# Invoke and relay stdout + exit code.
67+
ssh -o BatchMode=yes "$HOST" "powershell -NoProfile -ExecutionPolicy Bypass -File '$REMOTE_DIR/tests/distiller_tests/run.ps1' -Distiller '$ACRODIST' -Monolithic '$REMOTE_DIR/barcode.ps' $*"

tests/distiller_tests/run.ps1

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Barcode Writer in Pure PostScript
2+
# https://bwipp.terryburton.co.uk
3+
#
4+
# Copyright (c) 2004-2026 Terry Burton
5+
#
6+
#
7+
# Run the PostScript test suite against Adobe Acrobat Distiller. Each test
8+
# file runs in its own Distiller invocation since a test failure quits the
9+
# interpreter. Requires build/monolithic/barcode.ps (make monolithic) and a
10+
# Distiller install (see README.md).
11+
#
12+
# -Distiller <path> acrodist.exe (default: $env:ACRODIST)
13+
# -Monolithic <path> built barcode.ps (default: build/monolithic/barcode.ps)
14+
# -Timeout <sec> per-file timeout in seconds (default: 900)
15+
# -Skip <names> space-separated test names to skip
16+
# -Filter <glob> restrict to matching test names (default: *)
17+
18+
param(
19+
[string]$Distiller = $env:ACRODIST,
20+
[string]$Monolithic = "",
21+
[int]$Timeout = 900,
22+
[string]$Skip = "",
23+
[string]$Filter = "*"
24+
)
25+
26+
$ErrorActionPreference = "Continue"
27+
28+
$test = $PSScriptRoot
29+
$root = (Resolve-Path (Join-Path $test "..\..")).Path
30+
if (-not $Monolithic) { $Monolithic = Join-Path $root "build\monolithic\barcode.ps" }
31+
$testsdir = Join-Path $root "tests\ps_tests"
32+
$testutils = Join-Path $testsdir "test_utils.ps"
33+
$fontalias = Join-Path $test "fontalias.ps"
34+
35+
# Treat a missing or bogus path as "interpreter not available" and skip.
36+
if (-not $Distiller -or -not (Test-Path $Distiller)) {
37+
Write-Output "SKIPPED distiller not available"
38+
exit 0
39+
}
40+
if (-not (Test-Path $Monolithic)) {
41+
Write-Output "FAIL missing $Monolithic (run make first)"
42+
exit 1
43+
}
44+
45+
$distdir = Split-Path $Distiller
46+
$version = (Get-Item $Distiller).VersionInfo.FileVersion
47+
$skipset = @($Skip -split '\s+' | Where-Object { $_ })
48+
49+
$work = Join-Path $env:TEMP ("distiller_tests_" + [guid]::NewGuid().ToString("N"))
50+
New-Item -ItemType Directory -Force -Path $work | Out-Null
51+
52+
$faF = $fontalias -replace '\\','/'
53+
$tuF = $testutils -replace '\\','/'
54+
$moF = $Monolithic -replace '\\','/'
55+
56+
$files = Get-ChildItem (Join-Path $testsdir "$Filter.ps.test") | Sort-Object Name
57+
58+
Write-Output "Running tests/distiller_tests with Acrobat Distiller $version"
59+
Write-Output "$($files.Count) test files"
60+
61+
$pass = 0; $skipped = 0; $failed = 0
62+
try {
63+
foreach ($f in $files) {
64+
$name = $f.Name -replace '\.ps\.test$',''
65+
if ($skipset -contains $name) { Write-Output "SKIP $name"; $skipped++; continue }
66+
67+
$drv = Join-Path $work "$name.ps"
68+
$log = Join-Path $work "$name.log"
69+
$tfF = $f.FullName -replace '\\','/'
70+
# Each file runs in its own instance: fontalias supplies the base-14
71+
# font names, then the shared test utilities, library and test file.
72+
$driver = "%!PS-Adobe-3.0`n" +
73+
"($faF) run`n($tuF) run`n($moF) run`n($tfF) run`n" +
74+
"(FILE-PASSED\n) print flush`n"
75+
Set-Content -Path $drv -Value $driver -Encoding ASCII
76+
77+
$sw = [Diagnostics.Stopwatch]::StartNew()
78+
$p = Start-Process -FilePath $Distiller -ArgumentList "/N","/Q",$drv `
79+
-PassThru -WorkingDirectory $distdir -WindowStyle Hidden
80+
$ok = $p.WaitForExit($Timeout * 1000)
81+
$sw.Stop()
82+
$secs = "{0:0.00}" -f $sw.Elapsed.TotalSeconds
83+
84+
if (-not $ok) {
85+
try { $p.Kill() } catch {}
86+
Write-Output "FAIL $name (${secs}s)"
87+
Write-Output " timeout after ${Timeout}s"
88+
$failed++
89+
continue
90+
}
91+
92+
$t = if (Test-Path $log) { Get-Content $log -Raw } else { "" }
93+
if ($t -match "FILE-PASSED" -and
94+
$t -notmatch "testError|stackImbalance|dict leak|global VM leak|%%\[ Error") {
95+
Write-Output "PASS $name (${secs}s)"
96+
$pass++
97+
} else {
98+
Write-Output "FAIL $name (${secs}s)"
99+
($t -replace "`r","" -split "`n" | Select-Object -Last 40) |
100+
ForEach-Object { Write-Output $_ }
101+
$failed++
102+
}
103+
}
104+
} finally {
105+
Remove-Item $work -Recurse -Force -EA SilentlyContinue
106+
}
107+
108+
Write-Output "distiller tests: $pass passed, $skipped skipped, $failed failed"
109+
exit ([int]($failed -ne 0))

0 commit comments

Comments
 (0)