314 lines
8.0 KiB
Bash
Executable File
314 lines
8.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
WORKSPACE_ROOT="$(cd "$ROOT_DIR/.." && pwd)"
|
|
LINUXCNC_DIR="${LINUXCNC_DIR:-$WORKSPACE_ROOT/linuxcnc}"
|
|
NC_DIR="$LINUXCNC_DIR/nc_files"
|
|
BUILD_DIR="${NC_FILES_BUILD_DIR:-$ROOT_DIR/build/native/nc-files}"
|
|
RS274="${RS274:-$LINUXCNC_DIR/bin/rs274}"
|
|
TIMEOUT_SECONDS="${NC_FILES_TIMEOUT_SECONDS:-30}"
|
|
ONLY_FILTER=""
|
|
SCAN_ALL=0
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--all)
|
|
SCAN_ALL=1
|
|
shift
|
|
;;
|
|
--only)
|
|
[[ $# -ge 2 ]] || { echo "missing value for --only" >&2; exit 2; }
|
|
ONLY_FILTER="$2"
|
|
shift 2
|
|
;;
|
|
--help|-h)
|
|
cat <<EOF
|
|
Usage: $0 [--all] [--only PATH_SUBSTRING]
|
|
|
|
Runs LinuxCNC nc_files programs through LinuxCNC's rs274 standalone entry point.
|
|
The default suite covers the basic/example programs and common macro libraries.
|
|
Use --all to scan every .ngc under linuxcnc/nc_files.
|
|
|
|
Results are written under:
|
|
$BUILD_DIR
|
|
EOF
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "unknown argument: $1" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ ! -x "$RS274" ]]; then
|
|
echo "missing executable rs274: $RS274" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -d "$NC_DIR" ]]; then
|
|
echo "missing LinuxCNC nc_files directory: $NC_DIR" >&2
|
|
exit 1
|
|
fi
|
|
|
|
export LD_LIBRARY_PATH="$LINUXCNC_DIR/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
|
export PYTHONPATH="$LINUXCNC_DIR/lib/python${PYTHONPATH:+:$PYTHONPATH}"
|
|
|
|
mkdir -p "$BUILD_DIR"/{logs,out,wrapped,params}
|
|
SUMMARY_FILE="$BUILD_DIR/summary.tsv"
|
|
INI_FILE="$BUILD_DIR/test-rs274.ini"
|
|
TOOL_TABLE="$BUILD_DIR/test-tools.tbl"
|
|
PARAMETER_TEMPLATE="$BUILD_DIR/test.var.template"
|
|
printf 'path\tclass\tstatus\texpected_failure\texit_code\tseconds\toutput_lines\tstderr_summary\n' > "$SUMMARY_FILE"
|
|
|
|
relpath() {
|
|
local path="$1"
|
|
printf '%s\n' "${path#"$NC_DIR"/}"
|
|
}
|
|
|
|
safe_name() {
|
|
local rel="$1"
|
|
rel="${rel//\//_}"
|
|
rel="${rel// /_}"
|
|
rel="${rel//#/_}"
|
|
rel="${rel//=/}"
|
|
printf '%s\n' "$rel"
|
|
}
|
|
|
|
write_fixture_config() {
|
|
{
|
|
printf '[RS274NGC]\n'
|
|
printf 'PARAMETER_FILE = test.var\n'
|
|
printf 'SUBROUTINE_PATH = %s\n' "$(
|
|
find "$NC_DIR" -type d | sort | paste -sd ':' -
|
|
)"
|
|
printf 'OWORD_NARGS = 1\n'
|
|
printf 'FEATURES = 30\n'
|
|
printf '\n[DISPLAY]\n'
|
|
printf 'PROGRAM_PREFIX = %s\n' "$NC_DIR"
|
|
printf '\n[TRAJ]\n'
|
|
printf 'COORDINATES = X Y Z A B C U V W\n'
|
|
printf '\n[EMCIO]\n'
|
|
printf 'TOOL_TABLE = %s\n' "$TOOL_TABLE"
|
|
} > "$INI_FILE"
|
|
|
|
: > "$TOOL_TABLE"
|
|
local tool
|
|
for tool in $(seq 1 200); do
|
|
printf 'T%s P%s D0.125000 Z+0.000000 ; generated nc_files test tool\n' "$tool" "$tool" >> "$TOOL_TABLE"
|
|
done
|
|
|
|
{
|
|
printf '5161 0.0\n'
|
|
printf '5162 0.0\n'
|
|
printf '5163 0.0\n'
|
|
printf '5220 1\n'
|
|
printf '5221 0.0\n'
|
|
printf '5222 0.0\n'
|
|
printf '5223 0.0\n'
|
|
printf '5399 0.0\n'
|
|
} > "$PARAMETER_TEMPLATE"
|
|
}
|
|
|
|
has_program_end() {
|
|
local ngc="$1"
|
|
awk '
|
|
BEGIN { found = 0 }
|
|
/^[[:space:]]*%[[:space:]]*$/ { found = 1 }
|
|
{
|
|
line = tolower($0)
|
|
gsub(/\([^)]*\)/, "", line)
|
|
if (line ~ /(^|[[:space:]])m0*(2|30)([[:space:]]|$)/) {
|
|
found = 1
|
|
}
|
|
}
|
|
END { exit(found ? 0 : 1) }
|
|
' "$ngc"
|
|
}
|
|
|
|
is_default_basic_path() {
|
|
local rel="$1"
|
|
case "$rel" in
|
|
*/*)
|
|
case "$rel" in
|
|
gladevcp_lib/*.ngc|macros/lathe/*.ngc|macros/mill/*.ngc|ngcgui_lib/*.ngc)
|
|
return 0
|
|
;;
|
|
esac
|
|
return 1
|
|
;;
|
|
*.ngc)
|
|
return 0
|
|
;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
classify_program() {
|
|
local rel="$1"
|
|
local ngc="$2"
|
|
case "$rel" in
|
|
macros/*|ngcgui_lib/*|gladevcp_lib/*|remap-subroutines/*|remap_lib/*|probe/*)
|
|
printf 'macro_load\n'
|
|
return 0
|
|
;;
|
|
esac
|
|
if has_program_end "$ngc"; then
|
|
printf 'main\n'
|
|
else
|
|
printf 'macro_load\n'
|
|
fi
|
|
}
|
|
|
|
prepare_input() {
|
|
local class="$1"
|
|
local ngc="$2"
|
|
local wrapped="$3"
|
|
if [[ "$class" == "main" ]]; then
|
|
printf '%s\n' "$ngc"
|
|
return 0
|
|
fi
|
|
cp "$ngc" "$wrapped"
|
|
printf '\nM2\n' >> "$wrapped"
|
|
printf '%s\n' "$wrapped"
|
|
}
|
|
|
|
known_expected_failure() {
|
|
local rel="$1"
|
|
local combined="$2"
|
|
case "$rel" in
|
|
cone.ngc)
|
|
[[ "$combined" == *"Bad character 'w' used"* ]] && { printf 'five-axis-W-machine-context\n'; return 0; }
|
|
;;
|
|
g76.ngc|lathe-g76.ngc)
|
|
[[ "$combined" == *"Length of cutter compensation entry move is not greater than the tool radius"* ]] && { printf 'lathe-cutter-comp-tool-context\n'; return 0; }
|
|
;;
|
|
lathe_g70_71_demo.ngc)
|
|
[[ "$combined" == *"Arc move in concave corner cannot be reached by the tool without gouging"* ]] && { printf 'lathe-profile-tool-context\n'; return 0; }
|
|
;;
|
|
nestedcall.ngc)
|
|
[[ "$combined" == *"Unexpected character after O-word"* ]] && { printf 'upstream-oword-callsub-syntax-edge\n'; return 0; }
|
|
;;
|
|
nurbs/G5/Curva_Stella\ #1=1_G5.ngc)
|
|
[[ "$combined" == *"Unknown g code used"* && "$combined" == *"G2.2"* ]] && { printf 'nurbs-g22-entrypoint-edge\n'; return 0; }
|
|
;;
|
|
probe*.ngc|probe/*|smartprobe.ngc|tool-length-probe.ngc|touchoff.ngc)
|
|
[[ "$combined" == *"probe"* || "$combined" == *"G38"* ]] && { printf 'probe-runtime-context\n'; return 0; }
|
|
;;
|
|
plasmac/*|plasmatest.ngc)
|
|
[[ "$combined" == *"Unknown m code"* || "$combined" == *"Named parameter"* ]] && { printf 'plasmac-runtime-context\n'; return 0; }
|
|
;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
summarize_text() {
|
|
local file="$1"
|
|
if [[ ! -f "$file" ]]; then
|
|
return 0
|
|
fi
|
|
tr '\n\t' ' ' < "$file" | sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//' | cut -c 1-260
|
|
}
|
|
|
|
write_fixture_config
|
|
|
|
total=0
|
|
pass=0
|
|
fail=0
|
|
timeout_count=0
|
|
expected_fail=0
|
|
unexpected_fail=0
|
|
|
|
while IFS= read -r ngc; do
|
|
rel="$(relpath "$ngc")"
|
|
if [[ "$SCAN_ALL" -eq 0 ]] && ! is_default_basic_path "$rel"; then
|
|
continue
|
|
fi
|
|
if [[ -n "$ONLY_FILTER" && "$rel" != *"$ONLY_FILTER"* ]]; then
|
|
continue
|
|
fi
|
|
|
|
total=$((total + 1))
|
|
class="$(classify_program "$rel" "$ngc")"
|
|
safe="$(safe_name "$rel")"
|
|
wrapped="$BUILD_DIR/wrapped/$safe"
|
|
input="$(prepare_input "$class" "$ngc" "$wrapped")"
|
|
out_file="$BUILD_DIR/out/$safe.out"
|
|
stdout_file="$BUILD_DIR/logs/$safe.stdout"
|
|
stderr_file="$BUILD_DIR/logs/$safe.stderr"
|
|
param_file="$BUILD_DIR/params/$safe.var"
|
|
cp "$PARAMETER_TEMPLATE" "$param_file"
|
|
|
|
start_time="$(date +%s)"
|
|
set +e
|
|
(
|
|
cd "$WORKSPACE_ROOT"
|
|
INI_FILE_NAME="$INI_FILE" timeout "$TIMEOUT_SECONDS" "$RS274" \
|
|
-g \
|
|
-i "$INI_FILE" \
|
|
-t "$TOOL_TABLE" \
|
|
-v "$param_file" \
|
|
"$input" \
|
|
"$out_file"
|
|
) >"$stdout_file" 2>"$stderr_file"
|
|
rc=$?
|
|
set -e
|
|
end_time="$(date +%s)"
|
|
seconds=$((end_time - start_time))
|
|
|
|
status="FAIL"
|
|
if [[ "$rc" -eq 0 ]]; then
|
|
status="PASS"
|
|
pass=$((pass + 1))
|
|
elif [[ "$rc" -eq 124 || "$rc" -eq 137 ]]; then
|
|
status="TIMEOUT"
|
|
timeout_count=$((timeout_count + 1))
|
|
fail=$((fail + 1))
|
|
else
|
|
fail=$((fail + 1))
|
|
fi
|
|
|
|
combined="$(cat "$stdout_file" "$stderr_file" 2>/dev/null || true)"
|
|
expected=""
|
|
if [[ "$status" != "PASS" ]]; then
|
|
expected="$(known_expected_failure "$rel" "$combined" || true)"
|
|
if [[ -n "$expected" ]]; then
|
|
expected_fail=$((expected_fail + 1))
|
|
else
|
|
unexpected_fail=$((unexpected_fail + 1))
|
|
fi
|
|
fi
|
|
|
|
output_lines=0
|
|
[[ -f "$out_file" ]] && output_lines="$(wc -l < "$out_file" | tr -d '[:space:]')"
|
|
stderr_summary="$(summarize_text "$stderr_file")"
|
|
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
|
"$rel" \
|
|
"$class" \
|
|
"$status" \
|
|
"$expected" \
|
|
"$rc" \
|
|
"$seconds" \
|
|
"$output_lines" \
|
|
"$stderr_summary" >> "$SUMMARY_FILE"
|
|
done < <(find "$NC_DIR" -type f -name '*.ngc' | sort)
|
|
|
|
{
|
|
printf 'nc_files harness summary\n'
|
|
printf 'summary: %s\n' "$SUMMARY_FILE"
|
|
printf 'suite: %s\n' "$([[ "$SCAN_ALL" -eq 1 ]] && printf all || printf basic)"
|
|
printf 'total: %s\n' "$total"
|
|
printf 'pass: %s\n' "$pass"
|
|
printf 'fail: %s\n' "$fail"
|
|
printf 'timeout: %s\n' "$timeout_count"
|
|
printf 'expected_fail: %s\n' "$expected_fail"
|
|
printf 'unexpected_fail: %s\n' "$unexpected_fail"
|
|
} | tee "$BUILD_DIR/summary.txt"
|
|
|
|
if [[ "$unexpected_fail" -ne 0 ]]; then
|
|
echo "unexpected nc_files failures:" >&2
|
|
awk -F '\t' 'NR > 1 && $3 != "PASS" && $4 == "" { print $1 "\t" $3 "\t" $8 }' "$SUMMARY_FILE" >&2
|
|
exit 1
|
|
fi
|