#!/bin/sh
set -eu

# fd 3 is a stable copy of the real stderr, so run()'s trace always reaches
# the terminal even when a caller locally redirects 2>&1 to capture a
# command's own stderr.
exec 3>&2

# Echoes the command before running it, so logs show exactly what was executed
run() {
    echo "+ $*" >&3
    "$@"
}

echo "Testing tint-tools (tint, tint_info) against real WGSL shaders..."

SOURCE_DIR="$(pwd)"
SHADERS="$SOURCE_DIR/debian/tests/wgsl-shaders"

WORKDIR="${AUTOPKGTEST_TMP:-$(mktemp -d)}"
if [ -z "${AUTOPKGTEST_TMP:-}" ]; then
    trap 'rm -rf "$WORKDIR"' EXIT
fi

cd "$WORKDIR"

# tint: compile a compute shader to SPIR-V assembly (default output), then
# assemble + validate it with spirv-as/spirv-val -- confirms tint emitted
# genuinely spec-conformant SPIR-V
echo "Running tint on compute.wgsl, validating the SPIR-V it emits..."
run tint "$SHADERS/compute.wgsl" -o compute.spvasm
run spirv-as compute.spvasm -o compute.spv
run spirv-val compute.spv

# tint: cross-compile to GLSL, then validate with glslangValidator as a real
# compute shader -- confirms the emitted GLSL is actually valid
echo "Running tint --format glsl on bindings.wgsl, validating the GLSL it emits..."
run tint --format glsl "$SHADERS/bindings.wgsl" -o bindings.comp
run glslangValidator -S comp bindings.comp

# tint: compile a specific entry point of a multi-entry-point shader
# directly to a binary SPIR-V file, then validate the binary itself
echo "Running tint -ep vs_main on triangle.wgsl, validating the binary SPIR-V..."
run tint "$SHADERS/triangle.wgsl" -ep vs_main -o triangle.spv
run spirv-val triangle.spv

# tint: read WGSL from stdin with an explicit --input-format, validate the
# SPIR-V it emits -- confirms the "optional for files, mandatory for stdin"
# input-format contract actually works for its intended, documented use.
echo "Running tint --input-format wgsl - on stdin, validating the SPIR-V it emits..."
run tint --input-format wgsl -o stdin.spvasm - < "$SHADERS/compute.wgsl"
run spirv-as stdin.spvasm -o stdin.spv
run spirv-val stdin.spv

# tint: read from stdin without --input-format -- confirms the mandatory-
# format guard actually rejects it instead of crashing.
echo "Running tint - on stdin with no --input-format, expecting a clean error..."
if OUTPUT=$(run tint -o - - < "$SHADERS/compute.wgsl" 2>&1); then
    echo "Error: tint accepted stdin input with no --input-format" >&2
    exit 1
fi
echo "$OUTPUT" | grep -q "No input format given for data read from stdin" || {
    echo "Error: tint did not report the expected stdin error, got: $OUTPUT" >&2
    exit 1
}

# tint: same as above, but piped with no filename argument at all -- the
# natural `cat file | tint ...` usage, distinct from the explicit-dash form.
echo "Running cat compute.wgsl | tint --input-format wgsl, validating the SPIR-V it emits..."
cat "$SHADERS/compute.wgsl" | run tint --input-format wgsl -o piped.spvasm
run spirv-as piped.spvasm -o piped.spv
run spirv-val piped.spv

# tint: piped with no filename argument and no --input-format -- confirms
# the mandatory-format guard also rejects the bare-pipe form.
echo "Running cat compute.wgsl | tint with no --input-format, expecting a clean error..."
if OUTPUT=$(cat "$SHADERS/compute.wgsl" | run tint -o - 2>&1); then
    echo "Error: tint accepted piped stdin input with no --input-format" >&2
    exit 1
fi
echo "$OUTPUT" | grep -q "No input format given for data read from stdin" || {
    echo "Error: tint did not report the expected stdin error for piped input, got: $OUTPUT" >&2
    exit 1
}

# tint_info: reflect a compute shader's entry point and bindings, checking
# the exact expected values
echo "Running tint_info on compute.wgsl..."
OUTPUT=$(run tint_info "$SHADERS/compute.wgsl")
echo "$OUTPUT" | grep -q "Entry Point = main_image (compute)" || {
    echo "Error: tint_info did not report the expected entry point for compute.wgsl" >&2
    exit 1
}
echo "$OUTPUT" | grep -q "Workgroup Size (16, 16, 1)" || {
    echo "Error: tint_info reported the wrong workgroup size for compute.wgsl" >&2
    exit 1
}
echo "$OUTPUT" | grep -q "resource_type = WriteOnlyStorageTexture" || {
    echo "Error: tint_info reported the wrong binding type for compute.wgsl" >&2
    exit 1
}

# tint_info: reflect a multi-entry-point shader, checking both entry points
# and their stages exactly.
echo "Running tint_info on triangle.wgsl..."
OUTPUT=$(run tint_info "$SHADERS/triangle.wgsl")
echo "$OUTPUT" | grep -q "Entry Point = vs_main (vertex)" || {
    echo "Error: tint_info did not report vs_main as a vertex entry point for triangle.wgsl" >&2
    exit 1
}
echo "$OUTPUT" | grep -q "Entry Point = fs_main (fragment)" || {
    echo "Error: tint_info did not report fs_main as a fragment entry point for triangle.wgsl" >&2
    exit 1
}

# tint_info: JSON reflection output. Parse it with python3's json module to
# confirm it's genuinely well-formed JSON, then check the exact expected binding/struct content within the parsed data.
echo "Running tint_info --json on bindings.wgsl, validating it's well-formed JSON..."
run tint_info --json "$SHADERS/bindings.wgsl" > bindings.json
python3 -c "
import json, sys

with open('bindings.json') as f:
    data = json.load(f)

entry = data['entry_points'][0]
assert entry['name'] == 'main', entry['name']
assert entry['stage'] == 'compute', entry['stage']
assert entry['workgroup_size'] == [64, 1, 1], entry['workgroup_size']

bindings = {(b['group'], b['binding']): b['resource_type'] for b in entry['bindings']}
assert bindings[(0, 0)] == 'UniformBuffer', bindings
assert bindings[(0, 1)] == 'StorageBuffer', bindings

struct = data['structures'][0]
assert struct['name'] == 'Params', struct['name']
assert struct['size'] == 8, struct['size']
"

exit 0
