#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
BUILD_DIR="$ROOT_DIR/build/native"
GCODE_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode"
CANON_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon"
GCODE_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode_errors"
CANON_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon_errors"
NAMEDPARAM_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/namedparams.ini"
SPINDLE_ORIENT_OFFSET_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/spindle_orient_offset.ini"
DISABLE_FANUC_STYLE_SUB_INI_FIXTURE="$ROOT_DIR/tests/fixtures/ini/disable_fanuc_style_sub.ini"
VALIDATION_CACHE_KEY_FILE="$BUILD_DIR/native-probe-validation.input.sha256"
VALIDATION_CACHE_OK_FILE="$BUILD_DIR/native-probe-validation.ok"

"$ROOT_DIR/tools/verify_upstream_baseline.sh"
"$ROOT_DIR/tools/verify_vendor_sync.sh"
"$ROOT_DIR/tools/verify_no_standalone_cnc_semantics.sh"
"$ROOT_DIR/tools/verify_native_linuxcnc_fixture_baseline.sh"
"$ROOT_DIR/tools/build_native_probes.sh"
"$ROOT_DIR/tests/native/verify_sim_configs.sh"
"$ROOT_DIR/tests/native/verify_nc_files.sh"

hash_tree() {
  find "$@" -type f -print0 | sort -z | xargs -0 -r sha256sum
}

hash_build_validation_inputs() {
  find "$BUILD_DIR" -maxdepth 1 -type f \
    \( \
      -name '*.exitcode' \
      -o -name '*.run.stdout.log' \
      -o -name '*.run.stderr.log' \
      -o -name 'source-probes.tsv' \
      -o -name 'native-source-proof-summary.tsv' \
      -o -name 'native-runtime-probe-summary.tsv' \
      -o -name 'native-probes.ok' \
      -o -name 'linuxcnc_interp_minimal_harness' \
    \) \
    ! -name 'native-probe-validation.*' \
    -print0 | sort -z | xargs -0 -r sha256sum
}

native_probe_validation_fingerprint() {
  {
    sha256sum \
      "$ROOT_DIR/tests/native/verify_native_probes.sh" \
      "$ROOT_DIR/tools/source-manifest.txt"
    hash_tree \
      "$GCODE_FIXTURE_DIR" \
      "$CANON_FIXTURE_DIR" \
      "$GCODE_ERROR_FIXTURE_DIR" \
      "$CANON_ERROR_FIXTURE_DIR" \
      "$ROOT_DIR/tests/fixtures/ini"
    hash_build_validation_inputs
  } | sha256sum | awk '{ print $1 }'
}

NATIVE_PROBE_VALIDATION_FINGERPRINT="$(native_probe_validation_fingerprint)"
if [[ -f "$VALIDATION_CACHE_KEY_FILE" && -f "$VALIDATION_CACHE_OK_FILE" ]] &&
   [[ "$(tr -d '[:space:]' < "$VALIDATION_CACHE_KEY_FILE")" == "$NATIVE_PROBE_VALIDATION_FINGERPRINT" ]] &&
   [[ "$(tr -d '[:space:]' < "$VALIDATION_CACHE_OK_FILE")" == "$NATIVE_PROBE_VALIDATION_FINGERPRINT" ]]; then
  echo "native probe validation up to date"
  exit 0
fi

check_source_probe_coverage() {
  local manifest_sources="$BUILD_DIR/source-manifest-sources.sorted"
  local probe_sources="$BUILD_DIR/source-probe-sources.sorted"
  local duplicate_probe_sources="$BUILD_DIR/source-probe-sources.duplicates"

  awk '
    /^[[:space:]]*($|#)/ { next }
    /\.(c|cc)$/ { print }
  ' "$ROOT_DIR/tools/source-manifest.txt" | sort > "$manifest_sources"

  if [[ ! -f "$BUILD_DIR/source-probes.tsv" ]]; then
    echo "missing source probe map: $BUILD_DIR/source-probes.tsv" >&2
    exit 1
  fi

  cut -f1 "$BUILD_DIR/source-probes.tsv" | sort > "$probe_sources"
  uniq -d "$probe_sources" > "$duplicate_probe_sources"
  if [[ -s "$duplicate_probe_sources" ]]; then
    echo "duplicate source probe entries:" >&2
    cat "$duplicate_probe_sources" >&2
    exit 1
  fi

  local missing_sources
  missing_sources="$(comm -23 "$manifest_sources" "$probe_sources")"
  if [[ -n "$missing_sources" ]]; then
    echo "manifest source files without source probes:" >&2
    printf '%s\n' "$missing_sources" >&2
    exit 1
  fi

  local extra_sources
  extra_sources="$(comm -13 "$manifest_sources" "$probe_sources")"
  if [[ -n "$extra_sources" ]]; then
    echo "source probes not listed in manifest:" >&2
    printf '%s\n' "$extra_sources" >&2
    exit 1
  fi
}

check_exitcode() {
  local name="$1"
  local file="$BUILD_DIR/$name.exitcode"

  if [[ ! -f "$file" ]]; then
    echo "missing exitcode: $file" >&2
    exit 1
  fi

  local rc
  rc="$(tr -d '[:space:]' < "$file")"
  if [[ "$rc" != "0" ]]; then
    echo "$name failed with exit code $rc" >&2
    if [[ -f "$BUILD_DIR/$name.stderr.log" ]]; then
      sed -n '1,160p' "$BUILD_DIR/$name.stderr.log" >&2
    fi
    exit 1
  fi
}

check_native_source_proof_summary_row() {
  local boundary_class="$1"
  local proof_key="$2"
  local summary_file="$BUILD_DIR/native-source-proof-summary.tsv"

  local row
  row="$(awk -F'\t' -v boundary_class="$boundary_class" -v proof_key="$proof_key" '
    NR > 1 && $1 == boundary_class && $2 == proof_key { print; count++ }
    END {
      if (count != 1) {
        exit 1
      }
    }
  ' "$summary_file")" || {
    echo "missing native source proof summary row: $boundary_class $proof_key" >&2
    exit 1
  }

  local proof_value
  local execution_enabled
  local promotion_allowed
  local stdout_log
  IFS=$'\t' read -r _ _ proof_value execution_enabled promotion_allowed stdout_log <<< "$row"

  if [[ "$proof_value" != "1" ]]; then
    echo "$boundary_class native source proof is not complete: $proof_value" >&2
    exit 1
  fi
  if [[ "$execution_enabled" != "0" || "$promotion_allowed" != "0" ]]; then
    echo "$boundary_class unexpectedly enables execution or promotion" >&2
    exit 1
  fi
  if [[ ! -f "$BUILD_DIR/$stdout_log" ]]; then
    echo "$boundary_class summary references missing stdout log: $stdout_log" >&2
    exit 1
  fi
}

check_native_source_proof_summary() {
  local summary_file="$BUILD_DIR/native-source-proof-summary.tsv"

  if [[ ! -f "$summary_file" ]]; then
    echo "missing native source proof summary: $summary_file" >&2
    exit 1
  fi

  local expected_header
  expected_header=$'boundary_class\tproof_key\tproof_value\texecution_enabled\tpromotion_allowed\tstdout_log'
  if [[ "$(sed -n '1p' "$summary_file")" != "$expected_header" ]]; then
    echo "native source proof summary header drift: $summary_file" >&2
    exit 1
  fi

  local row_count
  row_count="$(awk 'END { print NR - 1 }' "$summary_file")"
  if [[ "$row_count" != "3" ]]; then
    echo "native source proof summary must contain exactly 3 proof rows, got $row_count" >&2
    exit 1
  fi

  check_native_source_proof_summary_row \
    L4-USER-M-PROCESS \
    millturn_user_m_native_source_state_proof
  check_native_source_proof_summary_row \
    L4-TOOL-DB \
    tool_db_native_source_protocol_proof
  check_native_source_proof_summary_row \
    L4-PYTHON-REMAP \
    python_remap_native_source_inventory_proof
}

check_native_runtime_probe_summary_row() {
  local boundary_class="$1"
  local boundary_kind="$2"
  local target="$3"
  local runtime_probe="$4"
  local expected_required_native_proof="$5"
  local expected_opt_in_env="$6"
  local summary_file="$BUILD_DIR/native-runtime-probe-summary.tsv"

  local row
  row="$(awk -F'\t' \
    -v boundary_class="$boundary_class" \
    -v boundary_kind="$boundary_kind" \
    -v target="$target" \
    -v runtime_probe="$runtime_probe" '
      NR > 1 && $1 == boundary_class && $2 == boundary_kind && $3 == target && $4 == runtime_probe {
        print
        count++
      }
      END {
        if (count != 1) {
          exit 1
        }
      }
    ' "$summary_file")" || {
    echo "missing native runtime probe summary row: $boundary_class $boundary_kind $target" >&2
    exit 1
  }

  local runtime_ready
  local source_proof_ready
  local required_native_proof
  local opt_in_env
  local probe_status
  local execution_enabled
  local promotion_allowed
  local missing_requirements
  local probe_note
  local stdout_log
  IFS=$'\t' read -r _ _ _ _ required_native_proof opt_in_env runtime_ready source_proof_ready probe_status execution_enabled promotion_allowed missing_requirements probe_note stdout_log <<< "$row"

  if [[ "$source_proof_ready" != "1" ]]; then
    echo "$target runtime probe lacks source proof readiness: $source_proof_ready" >&2
    exit 1
  fi
  if [[ "$required_native_proof" != "$expected_required_native_proof" ]]; then
    echo "$target runtime probe required native proof drift: $required_native_proof" >&2
    exit 1
  fi
  if [[ "$opt_in_env" != "$expected_opt_in_env" ]]; then
    echo "$target runtime probe opt-in env drift: $opt_in_env" >&2
    exit 1
  fi
  if [[ "$execution_enabled" != "0" || "$promotion_allowed" != "0" ]]; then
    echo "$target runtime probe unexpectedly enables execution or promotion" >&2
    exit 1
  fi
  if [[ ! "$probe_status" =~ ^(skipped_missing_host_runtime|ready_disabled_by_default|not_implemented_full_process_guard|runtime_state_probe_passed|runtime_protocol_probe_passed|runtime_lifecycle_probe_passed)$ ]]; then
    echo "$target runtime probe has invalid status: $probe_status" >&2
    exit 1
  fi
  if [[ "$runtime_ready" == "0" && "$probe_status" != "skipped_missing_host_runtime" ]]; then
    echo "$target runtime probe is not ready but did not skip: $probe_status" >&2
    exit 1
  fi
  if [[ "$runtime_ready" == "0" && "$probe_note" != *"missing_host_runtime"* ]]; then
    echo "$target runtime probe missing-runtime note drift: $probe_note" >&2
    exit 1
  fi
  if [[ "$runtime_ready" == "1" && "$missing_requirements" != "-" ]]; then
    echo "$target runtime probe is ready but lists missing requirements: $missing_requirements" >&2
    exit 1
  fi
  if [[ "$probe_status" == "ready_disabled_by_default" && "$probe_note" != *"set_${opt_in_env%%=*}"* ]]; then
    echo "$target runtime probe ready-disabled note drift: $probe_note" >&2
    exit 1
  fi
  if [[ "$probe_status" == runtime_*_passed && "$probe_note" != *"without_promotion"* ]]; then
    echo "$target runtime probe passed note drift: $probe_note" >&2
    exit 1
  fi
  if [[ "$probe_note" == "-" || -z "$probe_note" ]]; then
    echo "$target runtime probe lacks note" >&2
    exit 1
  fi
  if [[ ! -f "$BUILD_DIR/$stdout_log" ]]; then
    echo "$target runtime summary references missing stdout log: $stdout_log" >&2
    exit 1
  fi
}

check_native_runtime_probe_summary() {
  local summary_file="$BUILD_DIR/native-runtime-probe-summary.tsv"

  if [[ ! -f "$summary_file" ]]; then
    echo "missing native runtime probe summary: $summary_file" >&2
    exit 1
  fi

  local expected_header
  expected_header=$'boundary_class\tboundary_kind\ttarget\truntime_probe\trequired_native_proof\topt_in_env\truntime_ready\tsource_proof_ready\tprobe_status\texecution_enabled\tpromotion_allowed\tmissing_requirements\tprobe_note\tstdout_log'
  if [[ "$(sed -n '1p' "$summary_file")" != "$expected_header" ]]; then
    echo "native runtime probe summary header drift: $summary_file" >&2
    exit 1
  fi

  local row_count
  row_count="$(awk 'END { print NR - 1 }' "$summary_file")"
  if [[ "$row_count" != "3" ]]; then
    echo "native runtime probe summary must contain exactly 3 probe rows, got $row_count" >&2
    exit 1
  fi

  check_native_runtime_probe_summary_row \
    L4-USER-M-PROCESS \
    external_user_m_process \
    axis/vismach/millturn/example.ngc \
    linuxcnc_millturn_user_m_runtime_probe \
    native_runtime_state_probe_required \
    ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1
  check_native_runtime_probe_summary_row \
    L4-TOOL-DB \
    tool_database_process \
    axis/db_demo/base.ngc \
    linuxcnc_tool_db_runtime_probe \
    native_db_process_protocol_probe_required \
    ENABLE_TOOL_DB_RUNTIME_PROBE=1
  check_native_runtime_probe_summary_row \
    L4-PYTHON-REMAP \
    python_runtime \
    axis/remap/stop-lookahead/nc_files \
    linuxcnc_python_remap_runtime_probe \
    linuxcnc_python_runtime_lifecycle_probe_required \
    ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1
}

check_source_probe_coverage
check_native_source_proof_summary
check_native_runtime_probe_summary
check_exitcode linuxcnc_interp_state_probe
check_exitcode linuxcnc_emc_status_probe
check_exitcode linuxcnc_emc_status_probe.run
check_exitcode linuxcnc_inifile_source_probe
check_exitcode linuxcnc_5axis_remap_asset_probe
check_exitcode linuxcnc_5axis_remap_asset_probe.run
check_exitcode linuxcnc_millturn_user_m_boundary_probe
check_exitcode linuxcnc_millturn_user_m_boundary_probe.run
check_exitcode linuxcnc_millturn_user_m_runtime_probe.run
check_exitcode linuxcnc_tool_db_boundary_probe
check_exitcode linuxcnc_tool_db_boundary_probe.run
check_exitcode linuxcnc_python_remap_boundary_probe
check_exitcode linuxcnc_python_remap_boundary_probe.run
check_exitcode linuxcnc_python_remap_runtime_probe.run
check_exitcode linuxcnc_tp_api_probe
check_exitcode linuxcnc_tp_api_probe.run
check_exitcode linuxcnc_kinematics_probe
check_exitcode linuxcnc_kinematics_probe.run
check_exitcode linuxcnc_5axis_kinematics_probe
check_exitcode linuxcnc_5axis_kinematics_probe.run
check_exitcode linuxcnc_xyzac_trt_kinematics_probe
check_exitcode linuxcnc_xyzac_trt_kinematics_probe.run
check_exitcode linuxcnc_xyzbc_trt_kinematics_probe
check_exitcode linuxcnc_xyzbc_trt_kinematics_probe.run
check_exitcode linuxcnc_corexy_kinematics_probe
check_exitcode linuxcnc_corexy_kinematics_probe.run
check_exitcode linuxcnc_rotate_kinematics_probe
check_exitcode linuxcnc_rotate_kinematics_probe.run
check_exitcode linuxcnc_rose_kinematics_probe
check_exitcode linuxcnc_rose_kinematics_probe.run
check_exitcode linuxcnc_max_kinematics_probe
check_exitcode linuxcnc_max_kinematics_probe.run
check_exitcode linuxcnc_lineardelta_kinematics_probe
check_exitcode linuxcnc_lineardelta_kinematics_probe.run
check_exitcode linuxcnc_rotarydelta_kinematics_probe
check_exitcode linuxcnc_rotarydelta_kinematics_probe.run
check_exitcode linuxcnc_scorbot_kinematics_probe
check_exitcode linuxcnc_scorbot_kinematics_probe.run
check_exitcode linuxcnc_tripod_kinematics_probe
check_exitcode linuxcnc_tripod_kinematics_probe.run
check_exitcode linuxcnc_scara_kinematics_probe
check_exitcode linuxcnc_scara_kinematics_probe.run
check_exitcode linuxcnc_puma_kinematics_probe
check_exitcode linuxcnc_puma_kinematics_probe.run
check_exitcode linuxcnc_genser_kinematics_probe
check_exitcode linuxcnc_genser_kinematics_probe.run
check_exitcode linuxcnc_genhex_kinematics_probe
check_exitcode linuxcnc_genhex_kinematics_probe.run
check_exitcode linuxcnc_pentakins_kinematics_probe
check_exitcode linuxcnc_pentakins_kinematics_probe.run
for name in \
  linuxcnc_tp_tp_source_probe \
  linuxcnc_tp_tc_source_probe \
  linuxcnc_tp_tcq_source_probe \
  linuxcnc_tp_spherical_arc_source_probe \
  linuxcnc_tp_blendmath_source_probe \
  linuxcnc_tp_sp_scurve_source_probe \
  linuxcnc_tp_ruckig_wrapper_source_probe \
  linuxcnc_cruckig_block_source_probe \
  linuxcnc_cruckig_brake_source_probe \
  linuxcnc_cruckig_calculator_source_probe \
  linuxcnc_cruckig_cruckig_source_probe \
  linuxcnc_cruckig_input_parameter_source_probe \
  linuxcnc_cruckig_output_parameter_source_probe \
  linuxcnc_cruckig_profile_source_probe \
  linuxcnc_cruckig_roots_source_probe \
  linuxcnc_cruckig_trajectory_source_probe \
  linuxcnc_cruckig_position_first_step1_source_probe \
  linuxcnc_cruckig_position_first_step2_source_probe \
  linuxcnc_cruckig_position_second_step1_source_probe \
  linuxcnc_cruckig_position_second_step2_source_probe \
  linuxcnc_cruckig_position_third_step1_source_probe \
  linuxcnc_cruckig_position_third_step2_source_probe \
  linuxcnc_cruckig_velocity_second_step1_source_probe \
  linuxcnc_cruckig_velocity_second_step2_source_probe \
  linuxcnc_cruckig_velocity_third_step1_source_probe \
  linuxcnc_cruckig_velocity_third_step2_source_probe \
  linuxcnc_emcpose_source_probe \
  linuxcnc_posemath_source_probe \
  linuxcnc_posemath_c_source_probe \
  linuxcnc_sincos_source_probe; do
  check_exitcode "$name"
done
check_exitcode linuxcnc_namedparam_harness
check_exitcode linuxcnc_namedparam_harness.run
check_exitcode linuxcnc_interp_minimal_harness
check_exitcode linuxcnc_interp_minimal_harness.run
check_exitcode linuxcnc_interp_minimal_harness.do_while_break.run
check_exitcode linuxcnc_interp_minimal_harness.oword_bug315.run
check_exitcode linuxcnc_interp_minimal_harness.oword_bug315_p2.run
check_exitcode linuxcnc_interp_minimal_harness.interp_exists.run
check_exitcode linuxcnc_interp_minimal_harness.return_value.run
check_exitcode linuxcnc_interp_minimal_harness.subs_follow_main.run
check_exitcode linuxcnc_interp_minimal_harness.fractional_linenumbers.run
check_exitcode linuxcnc_interp_minimal_harness.cam_nisley_bare.run
check_exitcode linuxcnc_interp_minimal_harness.cam_nisley.run
check_exitcode linuxcnc_interp_minimal_harness.crazy_paths.run
check_exitcode linuxcnc_interp_minimal_harness.namedparam_bug424.run
check_exitcode linuxcnc_interp_minimal_harness.flowsnake.run
check_exitcode linuxcnc_interp_minimal_harness.inside_corners.run
check_exitcode linuxcnc_interp_minimal_harness.inverse_time_with_comp.run
check_exitcode linuxcnc_interp_minimal_harness.ccomp_lathe_comp.run
check_exitcode linuxcnc_interp_minimal_harness.ccomp_mill_g90g91g92.run
check_exitcode linuxcnc_interp_minimal_harness.ccomp_mill_line_arc_entry.run
check_exitcode linuxcnc_interp_minimal_harness.ccomp_mill_zchanges.run
check_exitcode linuxcnc_interp_minimal_harness.bad_a_in_canned_cycle.run
check_exitcode linuxcnc_interp_minimal_harness.bad_a_in_canned_cycle2.run
check_exitcode linuxcnc_interp_minimal_harness.bad_ccomp_arcexit.run
check_exitcode linuxcnc_interp_minimal_harness.bad_ccomp_gouging.run
check_exitcode linuxcnc_interp_minimal_harness.bad_exists_1.run
check_exitcode linuxcnc_interp_minimal_harness.bad_exists_2.run
check_exitcode linuxcnc_interp_minimal_harness.bad_exists_3.run
check_exitcode linuxcnc_interp_minimal_harness.bad_exists_4.run
check_exitcode linuxcnc_interp_minimal_harness.bad_exists_5.run
check_exitcode linuxcnc_interp_minimal_harness.bad_exists_6.run
check_exitcode linuxcnc_interp_minimal_harness.bad_exists_7.run
check_exitcode linuxcnc_interp_minimal_harness.bad_nested.run
check_exitcode linuxcnc_interp_minimal_harness.bad_no_feed_rate.run
check_exitcode linuxcnc_interp_minimal_harness.bad_no_ijr.run
check_exitcode linuxcnc_interp_minimal_harness.bad_probe_no_axes.run
check_exitcode linuxcnc_interp_minimal_harness.g33_1.run
check_exitcode linuxcnc_interp_minimal_harness.g6164.run
check_exitcode linuxcnc_interp_minimal_harness.rotation_abs_pts.run
check_exitcode linuxcnc_interp_minimal_harness.rotation_g28.run
check_exitcode linuxcnc_interp_minimal_harness.rotation_g53.run
check_exitcode linuxcnc_interp_minimal_harness.iniparam.run
check_exitcode linuxcnc_interp_minimal_harness.iniparam_failassign.run
check_exitcode linuxcnc_interp_minimal_harness.sub_call_from_sub.run
check_exitcode linuxcnc_interp_minimal_harness.sequence_number.run
check_exitcode linuxcnc_interp_minimal_harness.nested_sub_error.run
check_exitcode linuxcnc_interp_minimal_harness.nested_sub_in_file_error.run
check_exitcode linuxcnc_interp_minimal_harness.oword_unwind.run
check_exitcode linuxcnc_interp_minimal_harness.abort_hot_comment.run
check_exitcode linuxcnc_interp_minimal_harness.m19.run
check_exitcode linuxcnc_interp_minimal_harness.magic_comments_param_format.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_01_basics.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_02_variables.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_07_nested_subs.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_08_sub_follows_main.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_10_m98_p001.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_13_named_program_named.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_13_named_program_numbered.run
check_exitcode linuxcnc_interp_minimal_harness.m98m99_14_o_expression_call.run
check_exitcode linuxcnc_interp_minimal_harness.sim_axis_foam.run
check_exitcode linuxcnc_interp_minimal_harness.sim_bridgemill.run
check_exitcode linuxcnc_interp_minimal_harness.sim_geometry_xyzc.run
check_exitcode linuxcnc_interp_minimal_harness.sim_external_offsets_dyn.run
check_exitcode linuxcnc_interp_minimal_harness.g92_persistence_enabled.run
check_exitcode linuxcnc_interp_minimal_harness.g92_persistence_disabled.run
check_exitcode linuxcnc_parameter_file_harness
check_exitcode linuxcnc_parameter_file_harness.run
check_exitcode linuxcnc_interp_init_harness
check_exitcode linuxcnc_interp_init_harness.run
check_exitcode linuxcnc_remap_parse_harness
check_exitcode linuxcnc_remap_parse_harness.run
check_exitcode linuxcnc_remap_hal_sync_harness
check_exitcode linuxcnc_remap_hal_sync_harness.run
check_exitcode linuxcnc_5axis_remap_execute_harness
check_exitcode linuxcnc_5axis_remap_execute_harness.run
check_exitcode linuxcnc_duplicate_oword_remap_harness
check_exitcode linuxcnc_duplicate_oword_remap_harness.run
check_exitcode linuxcnc_indexer_harness
check_exitcode linuxcnc_indexer_harness.run
check_exitcode linuxcnc_rs274_compile_probe
check_exitcode linuxcnc_kins_util_source_probe
check_exitcode linuxcnc_cubic_source_probe
check_exitcode linuxcnc_trivkins_source_probe
check_exitcode linuxcnc_switchkins_source_probe
check_exitcode linuxcnc_userkfuncs_source_probe
check_exitcode linuxcnc_5axiskins_source_probe
check_exitcode linuxcnc_trtfuncs_source_probe
check_exitcode linuxcnc_xyzac_trt_kins_source_probe
check_exitcode linuxcnc_xyzbc_trt_kins_source_probe
check_exitcode linuxcnc_corexykins_source_probe
check_exitcode linuxcnc_rotatekins_source_probe
check_exitcode linuxcnc_rosekins_source_probe
check_exitcode linuxcnc_maxkins_source_probe
check_exitcode linuxcnc_lineardeltakins_source_probe
check_exitcode linuxcnc_rotarydeltakins_source_probe
check_exitcode linuxcnc_scorbot_kins_source_probe
check_exitcode linuxcnc_tripodkins_source_probe
check_exitcode linuxcnc_scarakins_source_probe
check_exitcode linuxcnc_pumakins_source_probe
check_exitcode linuxcnc_genhexkins_source_probe
check_exitcode linuxcnc_genserfuncs_source_probe
check_exitcode linuxcnc_genserkins_source_probe
check_exitcode linuxcnc_ugenserkins_source_probe
check_exitcode linuxcnc_pentakins_source_probe
check_exitcode linuxcnc_interp_convert_source_probe
check_exitcode linuxcnc_interp_read_source_probe
check_exitcode linuxcnc_interp_check_source_probe
check_exitcode linuxcnc_interp_execute_source_probe
check_exitcode linuxcnc_modal_state_source_probe
check_exitcode linuxcnc_interp_array_source_probe
check_exitcode linuxcnc_interp_internal_source_probe
check_exitcode linuxcnc_interp_arc_source_probe
check_exitcode linuxcnc_interp_inverse_source_probe
check_exitcode linuxcnc_interp_cycles_source_probe
check_exitcode linuxcnc_interp_g7x_source_probe
check_exitcode linuxcnc_interp_queue_source_probe
check_exitcode linuxcnc_interp_find_source_probe
check_exitcode linuxcnc_interp_namedparams_source_probe
check_exitcode linuxcnc_interp_write_source_probe
check_exitcode linuxcnc_interp_o_word_source_probe
check_exitcode linuxcnc_interp_remap_source_probe
check_exitcode linuxcnc_rs274ngc_pre_source_probe
check_exitcode linuxcnc_interp_base_source_probe
check_exitcode linuxcnc_gomath_source_probe
check_exitcode linuxcnc_tooldata_common_source_probe

INIPARAM_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.iniparam.run.stdout.log"
grep -Fq "file_open=0" "$INIPARAM_STDOUT"
grep -Fq "file_read_count=9" "$INIPARAM_STDOUT"
grep -Fq "file_execute_count=8" "$INIPARAM_STDOUT"
grep -Fq "file_saw_error=1" "$INIPARAM_STDOUT"
grep -Fq "file_error_text=Named parameter #<_ini[nosuchsection]nosuchname> not defined" "$INIPARAM_STDOUT"
grep -Fq "setup.current_x=10" "$INIPARAM_STDOUT"
grep -Fq "setup.current_y=20" "$INIPARAM_STDOUT"
grep -Fq "setup.current_z=30" "$INIPARAM_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=1 x=10 y=20 z=30" "$INIPARAM_STDOUT"
grep -Fq "canon_event=MESSAGE:  position now: 10.000000 20.000000 30.000000" "$INIPARAM_STDOUT"
grep -Fq "canon_event=MESSAGE:  not in INI: ######" "$INIPARAM_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$INIPARAM_STDOUT"

INIPARAM_FAILASSIGN_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.iniparam_failassign.run.stdout.log"
grep -Fq "file_open=0" "$INIPARAM_FAILASSIGN_STDOUT"
grep -Fq "file_saw_error=1" "$INIPARAM_FAILASSIGN_STDOUT"
grep -Fq "file_error_text=Cannot assign to read-only parameter #<_ini[vars]toolchange_x>" "$INIPARAM_FAILASSIGN_STDOUT"
if grep -Fq "canon_event=MESSAGE:  notreached" "$INIPARAM_FAILASSIGN_STDOUT"; then
  echo "iniparam-failassign unexpectedly reached post-assignment message" >&2
  exit 1
fi

EMC_STATUS_STDOUT="$BUILD_DIR/linuxcnc_emc_status_probe.run.stdout.log"
grep -Fq "emc_status_default_linear_units=1" "$EMC_STATUS_STDOUT"
grep -Fq "emc_status_default_units_visible=1" "$EMC_STATUS_STDOUT"
grep -Fq "emc_status_default_machine_units_mm=1" "$EMC_STATUS_STDOUT"
grep -Fq "emc_status_inch_machine_units_visible=1" "$EMC_STATUS_STDOUT"
grep -Fq "emc_status_mm_machine_units_visible=1" "$EMC_STATUS_STDOUT"

FIVEAXIS_REMAP_ASSET_STDOUT="$BUILD_DIR/linuxcnc_5axis_remap_asset_probe.run.stdout.log"
grep -Fq "bridgemill_ini_open=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "bridgemill_remap_M428_428remap=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "bridgemill_remap_M429_429remap=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "bridgemill_remap_M430_430remap=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "bridgemill_M428_remap_file_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "bridgemill_M429_remap_file_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "bridgemill_M430_remap_file_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzab_tdr_ini_open=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzab_tdr_remap_M428_428remap=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzab_tdr_remap_M429_429remap=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzab_tdr_M428_remap_file_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzab_tdr_M429_remap_file_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzac_trt_ini_open=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzac_trt_remap_M428_428remap=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzac_trt_remap_M429_429remap=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzac_trt_remap_M430_430remap=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzbc_trt_ini_open=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzbc_trt_remap_M428_428remap=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzbc_trt_remap_M429_429remap=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzbc_trt_remap_M430_430remap=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzab_tdr_switch_mcodes_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzac_trt_switch_mcodes_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "xyzbc_trt_switch_mcodes_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "fiveaxis_remap_assets_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"

MILLTURN_USER_M_BOUNDARY_STDOUT="$BUILD_DIR/linuxcnc_millturn_user_m_boundary_probe.run.stdout.log"
grep -Fq "millturn_ini_open=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_RS274NGC.USER_M_PATH_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_RS274NGC.SUBROUTINE_PATH_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_DISPLAY.PYVCP_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_HAL.HALUI_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_state_mode=mill" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_guard_pin=kinstype.is-0" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_script_tcl_hal_runtime=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_remap_calls_user_m_process=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_switchkins_output_pin=motion.analog-out-03" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_switchkins_target=0" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_work_offset_pocket=P7" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_work_offset_words=X-290 Y0 Z-160 A0" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_active_g5x=G59.1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_calls_M128=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_source_transition_from_linuxcnc=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_X_AXIS_X.MIN_LIMIT_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_X_AXIS_X.MAX_LIMIT_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_Z_AXIS_Z.MAX_LIMIT_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_source_state_targets_from_linuxcnc=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_state_mode=turn" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_guard_pin=kinstype.is-1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_script_tcl_hal_runtime=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_remap_calls_user_m_process=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_switchkins_output_pin=motion.analog-out-03" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_switchkins_target=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_work_offset_pocket=P8" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_work_offset_words=X-160 Y0 Z-290 A0" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_active_g5x=G59.2" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_calls_M129=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_source_transition_from_linuxcnc=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_X_AXIS_X.MIN_LIMIT_TURN_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_Z_AXIS_Z.MAX_LIMIT_TURN_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_source_state_targets_from_linuxcnc=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_user_m_native_source_state_proof=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_user_m_execution_enabled=0" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_user_m_promotion_allowed=0" "$MILLTURN_USER_M_BOUNDARY_STDOUT"

MILLTURN_USER_M_RUNTIME_STDOUT="$BUILD_DIR/linuxcnc_millturn_user_m_runtime_probe.run.stdout.log"
grep -Fq "millturn_user_m_runtime_requirements=" "$MILLTURN_USER_M_RUNTIME_STDOUT"
grep -Fq "millturn_user_m_source_proof_ready=1" "$MILLTURN_USER_M_RUNTIME_STDOUT"
grep -Fq "millturn_user_m_execution_enabled=0" "$MILLTURN_USER_M_RUNTIME_STDOUT"
grep -Fq "millturn_user_m_promotion_allowed=0" "$MILLTURN_USER_M_RUNTIME_STDOUT"
grep -Fq "millturn_user_m_runtime_probe_note=" "$MILLTURN_USER_M_RUNTIME_STDOUT"
if grep -Fq "millturn_user_m_runtime_ready=1" "$MILLTURN_USER_M_RUNTIME_STDOUT"; then
  grep -Eq "millturn_user_m_runtime_probe_status=(ready_disabled_by_default|runtime_state_probe_passed)" \
    "$MILLTURN_USER_M_RUNTIME_STDOUT"
  if grep -Fq "millturn_user_m_runtime_probe_status=runtime_state_probe_passed" "$MILLTURN_USER_M_RUNTIME_STDOUT"; then
    grep -Fq "millturn_user_m_M128_runtime_state_ok=1" "$MILLTURN_USER_M_RUNTIME_STDOUT"
    grep -Fq "millturn_user_m_M129_runtime_state_ok=1" "$MILLTURN_USER_M_RUNTIME_STDOUT"
  fi
else
  grep -Fq "millturn_user_m_runtime_ready=0" "$MILLTURN_USER_M_RUNTIME_STDOUT"
  grep -Fq "millturn_user_m_runtime_probe_status=skipped_missing_host_runtime" \
    "$MILLTURN_USER_M_RUNTIME_STDOUT"
fi

TOOL_DB_BOUNDARY_STDOUT="$BUILD_DIR/linuxcnc_tool_db_boundary_probe.run.stdout.log"
grep -Fq "db_nonran_ini_open=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "db_nonran_EMCIO.RANDOM_TOOLCHANGER_ok=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "db_nonran_EMCIO.DB_PROGRAM_ok=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "db_nonran_tool_table_ignored_with_db_program=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_task_db_mode_init=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_task_ignores_tool_table_with_db_program=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_task_load_unload_notify=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_child_process_boundary=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_program_executable_check=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_v2_1_handshake=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_getall_g_until_fini=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_notify_l_u_p_protocol=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_program_tooldb_module=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_program_nonran_callbacks=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_program_nonran_state_targets=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_program_reload_rules=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_native_source_protocol_proof=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_execution_enabled=0" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_promotion_allowed=0" "$TOOL_DB_BOUNDARY_STDOUT"

TOOL_DB_RUNTIME_STDOUT="$BUILD_DIR/linuxcnc_tool_db_runtime_probe.run.stdout.log"
grep -Fq "tool_db_runtime_requirements=" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_source_proof_ready=1" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_execution_enabled=0" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_promotion_allowed=0" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_runtime_probe_note=" "$TOOL_DB_RUNTIME_STDOUT"
if grep -Fq "tool_db_runtime_ready=1" "$TOOL_DB_RUNTIME_STDOUT"; then
  grep -Eq "tool_db_runtime_probe_status=(ready_disabled_by_default|runtime_protocol_probe_passed)" \
    "$TOOL_DB_RUNTIME_STDOUT"
  if grep -Fq "tool_db_runtime_probe_status=runtime_protocol_probe_passed" "$TOOL_DB_RUNTIME_STDOUT"; then
    grep -Fq "tool_db_runtime_protocol_probe_ok=1" "$TOOL_DB_RUNTIME_STDOUT"
    grep -Fq "tool_db_get_all_fini=1" "$TOOL_DB_RUNTIME_STDOUT"
    grep -Fq "tool_db_put_tool_update_state_ok=1" "$TOOL_DB_RUNTIME_STDOUT"
    grep -Fq "tool_db_load_spindle_state_ok=1" "$TOOL_DB_RUNTIME_STDOUT"
    grep -Fq "tool_db_unload_spindle_state_ok=1" "$TOOL_DB_RUNTIME_STDOUT"
    grep -Fq "tool_db_persistence_state_ok=1" "$TOOL_DB_RUNTIME_STDOUT"
  fi
else
  grep -Fq "tool_db_runtime_ready=0" "$TOOL_DB_RUNTIME_STDOUT"
  grep -Fq "tool_db_runtime_probe_status=skipped_missing_host_runtime" \
    "$TOOL_DB_RUNTIME_STDOUT"
fi

PYTHON_REMAP_BOUNDARY_STDOUT="$BUILD_DIR/linuxcnc_python_remap_boundary_probe.run.stdout.log"
grep -Fq "python_runtime_pycall_dispatch=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_runtime_remap_phases=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_runtime_generator_finish=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_runtime_callable_lookup=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_runtime_execute_runtime=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_plugin_initializes_python=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_plugin_toplevel_exec_file=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_plugin_ini_python_path=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_plugin_callable_invoke=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_plugin_reload_on_change=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_family_axis_laser_inventory=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_family_twp_nutating_inventory=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_family_axis_remap_cycle_inventory=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_family_gmoccapy_stdglue_inventory=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_remap_native_source_inventory_proof=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_remap_execution_enabled=0" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_remap_promotion_allowed=0" "$PYTHON_REMAP_BOUNDARY_STDOUT"

PYTHON_REMAP_RUNTIME_STDOUT="$BUILD_DIR/linuxcnc_python_remap_runtime_probe.run.stdout.log"
grep -Fq "python_remap_runtime_fixture_family=axis/remap/stop-lookahead/nc_files" \
  "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_runtime_fixture_id=stop_lookahead_python_runtime_lifecycle" \
  "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_runtime_fixture_scope=no_python_callables_no_ngc_only_subpaths" \
  "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_runtime_requirements=" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_runtime_modules=axis/remap/stop-lookahead/python/remap.py,axis/remap/stop-lookahead/python/toplevel.py" \
  "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_source_proof_ready=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_execution_enabled=0" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_promotion_allowed=0" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_runtime_probe_note=" "$PYTHON_REMAP_RUNTIME_STDOUT"
if grep -Fq "python_remap_runtime_ready=1" "$PYTHON_REMAP_RUNTIME_STDOUT"; then
  grep -Eq "python_remap_runtime_probe_status=(ready_disabled_by_default|runtime_lifecycle_probe_passed)" \
    "$PYTHON_REMAP_RUNTIME_STDOUT"
  if grep -Fq "python_remap_runtime_probe_status=runtime_lifecycle_probe_passed" "$PYTHON_REMAP_RUNTIME_STDOUT"; then
    grep -Fq "python_remap_runtime_lifecycle_probe_ok=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
    grep -Fq "python_remap_lifecycle_interpreter_sentinel_ok=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
    grep -Fq "python_remap_lifecycle_toplevel_imported=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
    grep -Fq "python_remap_lifecycle_remap_imported=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
    grep -Fq "python_remap_lifecycle_callable_lookup_ok=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
    grep -Fq "python_remap_lifecycle_generator_returned=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
    grep -Fq "python_remap_lifecycle_generator_first_yield=2" "$PYTHON_REMAP_RUNTIME_STDOUT"
    grep -Fq "python_remap_lifecycle_generator_finish_ok=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
  fi
else
  grep -Fq "python_remap_runtime_ready=0" "$PYTHON_REMAP_RUNTIME_STDOUT"
  grep -Fq "python_remap_runtime_probe_status=skipped_missing_host_runtime" \
    "$PYTHON_REMAP_RUNTIME_STDOUT"
fi

REMAP_PARSE_STDOUT="$BUILD_DIR/linuxcnc_remap_parse_harness.run.stdout.log"

check_remap_descriptor() {
  local machine="$1"
  local mcode="$2"

  grep -Fq "${machine}_${mcode}_name_lookup=1" "$REMAP_PARSE_STDOUT"
  grep -Fq "${machine}_${mcode}_mcode_lookup=1" "$REMAP_PARSE_STDOUT"
  grep -Fq "${machine}_${mcode}_ngc=1" "$REMAP_PARSE_STDOUT"
  grep -Fq "${machine}_${mcode}_modalgroup10=1" "$REMAP_PARSE_STDOUT"
  grep -Fq "${machine}_${mcode}_no_python=1" "$REMAP_PARSE_STDOUT"
  grep -Fq "${machine}_${mcode}_linuxcnc_mcode_remappable=1" "$REMAP_PARSE_STDOUT"
}

grep -Fq "xyzac_trt_ini_open=1" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzac_trt_parse_remap_1=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzac_trt_parse_remap_2=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzac_trt_parse_remap_3=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzac_trt_parsed_remap_count=3" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzac_trt_m_remapped_count=3" "$REMAP_PARSE_STDOUT"
check_remap_descriptor "xyzac_trt" "M428"
check_remap_descriptor "xyzac_trt" "M429"
check_remap_descriptor "xyzac_trt" "M430"
grep -Fq "xyzac_trt_linuxcnc_ngc_remaps_parsed=1" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_ini_open=1" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_parse_remap_1=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_parse_remap_2=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_parse_remap_3=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_parsed_remap_count=3" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzbc_trt_m_remapped_count=3" "$REMAP_PARSE_STDOUT"
check_remap_descriptor "xyzbc_trt" "M428"
check_remap_descriptor "xyzbc_trt" "M429"
check_remap_descriptor "xyzbc_trt" "M430"
grep -Fq "xyzbc_trt_linuxcnc_ngc_remaps_parsed=1" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzab_tdr_ini_open=1" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzab_tdr_parse_remap_1=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzab_tdr_parse_remap_2=0" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzab_tdr_parsed_remap_count=2" "$REMAP_PARSE_STDOUT"
grep -Fq "xyzab_tdr_m_remapped_count=2" "$REMAP_PARSE_STDOUT"
check_remap_descriptor "xyzab_tdr" "M428"
check_remap_descriptor "xyzab_tdr" "M429"
grep -Fq "xyzab_tdr_linuxcnc_ngc_remaps_parsed=1" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_ini_open=1" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_parse_remap_1=0" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_parse_remap_2=0" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_parse_remap_3=0" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_parsed_remap_count=3" "$REMAP_PARSE_STDOUT"
grep -Fq "bridgemill_m_remapped_count=3" "$REMAP_PARSE_STDOUT"
check_remap_descriptor "bridgemill" "M428"
check_remap_descriptor "bridgemill" "M429"
check_remap_descriptor "bridgemill" "M430"
grep -Fq "bridgemill_linuxcnc_ngc_remaps_parsed=1" "$REMAP_PARSE_STDOUT"
grep -Fq "fiveaxis_linuxcnc_remap_parse_path=1" "$REMAP_PARSE_STDOUT"

REMAP_HAL_SYNC_STDOUT="$BUILD_DIR/linuxcnc_remap_hal_sync_harness.run.stdout.log"
grep -Fq "_hal[motion.switchkins-type]: rc=0 found=1 value=0" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "m68_to_tcp=0" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "_hal[motion.switchkins-type]: rc=0 found=1 value=1" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "_hal[motion.analog-out-03]: rc=0 found=1 value=1" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "m66_sync=2" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "m66_input_flag=1" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "m66_input_index=0" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "m66_input_digital=0" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "m68_to_identity=0" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "canon_has_m68_tcp=1" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "canon_has_m66_sync=1" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "canon_has_m68_identity=1" "$REMAP_HAL_SYNC_STDOUT"
grep -Fq "fiveaxis_remap_hal_sync_boundary=1" "$REMAP_HAL_SYNC_STDOUT"

FIVEAXIS_REMAP_EXECUTE_STDOUT="$BUILD_DIR/linuxcnc_5axis_remap_execute_harness.run.stdout.log"
grep -Fq "xyzac_trt_ini_open=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_remaps_ready=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_M429_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_M429_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_M428_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_M428_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_M430_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_M430_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_linuxcnc_remap_execute_path=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_ini_open=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_remaps_ready=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_M429_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_M429_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_M428_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_M428_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_M430_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_M430_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_linuxcnc_remap_execute_path=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_ini_open=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_parsed_remap_count=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_remaps_ready=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_M429_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_M429_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_M428_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_M428_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_linuxcnc_remap_execute_path=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_ini_open=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_parsed_remap_count=3" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_remaps_ready=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_M429_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_M429_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill__hal[motion.switchkins-type]: rc=0 found=1 value=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_M428_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_M428_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_M430_execute_0=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_M430_execute_1=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill__hal[motion.switchkins-type]: rc=0 found=1 value=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_linuxcnc_remap_execute_path=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_file_read_count=620" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_file_execute_count=620" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_file_finish_count=21" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_file_final_rc=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_xyzbc_switchkins_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_xyzbc_switchkins_file_read_count=620" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_xyzbc_switchkins_file_execute_count=620" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_xyzbc_switchkins_file_finish_count=21" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_xyzbc_switchkins_file_final_rc=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_xyzbc_switchkins_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_xyzbc_switchkins__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_xyzbc_switchkins_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_1_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_1_file_read_count=248" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_1_file_execute_count=248" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_1_file_finish_count=9" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_1_file_final_rc=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_1_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_1__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_1_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_file_read_count=120" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_file_execute_count=120" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_file_finish_count=3" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_file_final_rc=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_2_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_3_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_3_file_read_count=86" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_3_file_execute_count=86" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_3_file_finish_count=3" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_3_file_final_rc=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_3_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_3__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_xyzac_switchkins_test_3_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_boat_xyzac_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_boat_xyzac_file_read_count=1927" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_boat_xyzac_file_execute_count=1927" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_boat_xyzac_file_finish_count=3" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_boat_xyzac_file_final_rc=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_boat_xyzac_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_boat_xyzac__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_boat_xyzac_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_boat_xyzbc_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_boat_xyzbc_file_read_count=1913" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_boat_xyzbc_file_execute_count=1913" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_boat_xyzbc_file_finish_count=3" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_boat_xyzbc_file_final_rc=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_boat_xyzbc_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_boat_xyzbc__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzbc_trt_boat_xyzbc_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_impeller_7bl_xyzac_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_impeller_7bl_xyzac_file_read_count=4558" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_impeller_7bl_xyzac_file_execute_count=4558" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_impeller_7bl_xyzac_file_finish_count=2" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_impeller_7bl_xyzac_file_final_rc=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_impeller_7bl_xyzac_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_impeller_7bl_xyzac__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzac_trt_impeller_7bl_xyzac_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_xyzab_tdr_demo_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_xyzab_tdr_demo_file_read_count=97" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_xyzab_tdr_demo_file_execute_count=97" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_xyzab_tdr_demo_file_finish_count=3" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_xyzab_tdr_demo_file_final_rc=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_xyzab_tdr_demo_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_xyzab_tdr_demo__hal[motion.switchkins-type]: rc=0 found=1 value=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "xyzab_tdr_xyzab_tdr_demo_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_bridgemill_5axisgui_file_open=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_bridgemill_5axisgui_file_read_count=2197" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_bridgemill_5axisgui_file_execute_count=2197" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_bridgemill_5axisgui_file_finish_count=137" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_bridgemill_5axisgui_file_final_rc=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_bridgemill_5axisgui_file_reached_exit=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_bridgemill_5axisgui__hal[motion.switchkins-type]: rc=0 found=1 value=0" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "bridgemill_bridgemill_5axisgui_linuxcnc_demo_file_execute=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"
grep -Fq "fiveaxis_linuxcnc_remap_execute_path=1" "$FIVEAXIS_REMAP_EXECUTE_STDOUT"

DUPLICATE_OWORD_REMAP_STDOUT="$BUILD_DIR/linuxcnc_duplicate_oword_remap_harness.run.stdout.log"
grep -Fq "duplicate_oword_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_subroutine_path_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_parse_remap_2=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_parsed_remap_count=2" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_remaps_ready=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_file_open=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_file_read_count=31" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_file_execute_count=31" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_file_finish_count=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_file_final_rc=3" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_file_reached_endfile=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_canon_event=MESSAGE:  executing m207:  run remapped m208 before o<problem>" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_canon_event=MESSAGE:  rm207 running remapped m208" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_canon_event=MESSAGE:  rm208 starting and done" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_canon_event=FINISH" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "duplicate_oword_linuxcnc_regression_path=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_parsed_remap_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_file_open=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_file_saw_error=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_canon_event=MESSAGE:  M400 call_level= 1.000000 remap_level=1.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_canon_event=MESSAGE:  P word set: 47.110000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_canon_event=MESSAGE:  Q word set: 8.150000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_0_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_file_execute_1=5" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_file_error_text=user-defined M400: missing: Q" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_file_saw_error=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_1_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_file_execute_1=5" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_file_error_text=user-defined M400: missing: P,Q" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_file_saw_error=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_args_2_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_file_read_5=5" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_file_error_text=Attempt to divide by zero" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_file_saw_error=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_canon_event=MESSAGE:  before M400: call_level= 0.000000 remap_level=0.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_canon_event=MESSAGE:  in rm400: call_level= 1.000000 remap_level=1.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_canon_event=MESSAGE:  after failed M400: call_level= 0.000000 remap_level=0.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "fail_body_ngc_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_subroutine_path_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_parsed_remap_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_remaps_ready=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_file_open=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_file_read_count=4" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_file_execute_count=4" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_file_finish_count=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_file_final_rc=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_file_reached_exit=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_canon_event=MESSAGE: m400 call_level=1.000000 remap_level=1.000000 line=2.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "m30_interaction_linuxcnc_regression_path=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_subroutine_path_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_parse_remap_2=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_parse_remap_3=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_parse_remap_4=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_parsed_remap_count=4" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_remaps_ready=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_file_open=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_file_read_count=21" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_file_execute_count=21" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_file_finish_count=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_file_final_rc=3" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_file_reached_endfile=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=MESSAGE: main call_level=0.000000 remap_level=0.000000 line=2.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=MESSAGE: m400 handler pre call_level=1.000000 remap_level=1.000000 line=2.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=MESSAGE: m401 handler pre call_level=2.000000 remap_level=2.000000 line=2.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=MESSAGE: m402 handler pre call_level=3.000000 remap_level=3.000000 line=2.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=MESSAGE: m403 handler pre call_level=4.000000 remap_level=4.000000 line=2.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=MESSAGE: m400 handler post call_level=1.000000 remap_level=1.000000 line=4.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_canon_event=FINISH" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "nested_remaps_oword_linuxcnc_regression_path=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_subroutine_path_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_parsed_remap_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_remaps_ready=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_file_open=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_file_read_count=19" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_file_execute_count=19" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_file_finish_count=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_file_final_rc=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_file_reached_exit=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_canon_event=MESSAGE:  in rg881: n_args=3.000000 [1.000000] [2.000000] [3.000000] [0.000000] [0.000000]" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_canon_event=MESSAGE:  in rg881: n_args=4.000000 [1.000000] [2.000000] [3.000000] [4.000000] [0.000000]" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_canon_event=MESSAGE:  in rg881: n_args=4.000000 [1.000000] [2.000000] [3.000000] [5.000000] [0.000000]" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_canon_event=MESSAGE:  in rg881: n_args=5.000000 [1.000000] [2.000000] [3.000000] [4.000000] [5.000000]" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "posargs_0_linuxcnc_regression_path=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_subroutine_path_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_2=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_3=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_4=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_5=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_6=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parse_remap_7=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_parsed_remap_count=7" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_remaps_ready=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_file_open=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_file_read_count=10084" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_file_execute_count=10084" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_file_final_rc=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_file_reached_exit=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE:  seq=1.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE:  seq=5.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE:  seq=6.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE:  seq=7.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE:  seq=8.000000 - reset to 1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE:  seq=8.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE:  seq=9.000000" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=MESSAGE:  seq=10.000000 - reset to 1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_canon_event=PROGRAM_END" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_linuxcnc_remap_file_execute=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "sequencing_linuxcnc_regression_path=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_ini_open=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_subroutine_path_count=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_parse_remap_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_parse_remap_7=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_parsed_remap_count=7" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_mdi_M66 P1_execute_0=2" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_mdi_M66 P1_execute_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_mdi_M66 E1 L0_execute_0=2" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_mdi_M66 E1 L0_execute_1=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_parameter_5399=42.13" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_parameter_5399=-13.42" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=SET_MOTION_OUTPUT_BIT index=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=CLEAR_MOTION_OUTPUT_BIT index=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=SET_AUX_OUTPUT_BIT index=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=CLEAR_AUX_OUTPUT_BIT index=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=WAIT index=0 input_type=1 wait_type=0 timeout=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=WAIT index=0 input_type=0 wait_type=0 timeout=0" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=SET_MOTION_OUTPUT_VALUE index=0 value=42.13" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_canon_event=SET_AUX_OUTPUT_VALUE index=0 value=-13.42" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "remap_io_ngc_linuxcnc_mdi_sequence=1" "$DUPLICATE_OWORD_REMAP_STDOUT"
grep -Fq "linuxcnc_upstream_remap_regression_path=1" "$DUPLICATE_OWORD_REMAP_STDOUT"

NAMEDPARAM_STDOUT="$BUILD_DIR/linuxcnc_namedparam_harness.run.stdout.log"
grep -Fq "init_named_parameters=0" "$NAMEDPARAM_STDOUT"
grep -Fq "global_named_count=57" "$NAMEDPARAM_STDOUT"
grep -Fq "_metric_machine: rc=0 found=1 value=1" "$NAMEDPARAM_STDOUT"
grep -Fq "_motion_mode: rc=0 found=1 value=10" "$NAMEDPARAM_STDOUT"
grep -Fq "_metric: rc=0 found=1 value=1" "$NAMEDPARAM_STDOUT"
grep -Fq "_feed: rc=0 found=1 value=123.45" "$NAMEDPARAM_STDOUT"
grep -Fq "_rpm: rc=0 found=1 value=678.9" "$NAMEDPARAM_STDOUT"
grep -Fq "_x: rc=0 found=1 value=1.25" "$NAMEDPARAM_STDOUT"
grep -Fq "_current_tool: rc=0 found=1 value=12" "$NAMEDPARAM_STDOUT"
grep -Fq "_ini[traj]max_linear_velocity: rc=0 found=1 value=35" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.pin-bit]: rc=0 found=1 value=1" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.signal-float]: rc=0 found=1 value=98.25" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.param-s32]: rc=0 found=1 value=-17" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.pin-u32]: rc=0 found=1 value=1.23457e+08" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.signal-s64]: rc=0 found=1 value=-9e+09" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.param-u64]: rc=0 found=1 value=9e+09" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.disconnected-float]: rc=0 found=1 value=12.5" "$NAMEDPARAM_STDOUT"
grep -Fq "_hal[standalone.missing]: rc=0 found=0 value=0" "$NAMEDPARAM_STDOUT"

PARAMETER_FILE_STDOUT="$BUILD_DIR/linuxcnc_parameter_file_harness.run.stdout.log"
grep -Fq "restore_existing=0" "$PARAMETER_FILE_STDOUT"
grep -Fq "parameter_5161=10.5" "$PARAMETER_FILE_STDOUT"
grep -Fq "parameter_5162=20.25" "$PARAMETER_FILE_STDOUT"
grep -Fq "parameter_5220=1" "$PARAMETER_FILE_STDOUT"
grep -Fq "parameter_5221=2.25" "$PARAMETER_FILE_STDOUT"
grep -Fq "parameter_5399=44" "$PARAMETER_FILE_STDOUT"
grep -Fq "restore_missing_file=0" "$PARAMETER_FILE_STDOUT"
grep -Fq "restore_out_of_order=5" "$PARAMETER_FILE_STDOUT"
grep -Fq "restore_out_of_order_error=Parameter file out of order" "$PARAMETER_FILE_STDOUT"
grep -Fq "restore_missing_required=0" "$PARAMETER_FILE_STDOUT"
grep -Fq "missing_required_5161=3.5" "$PARAMETER_FILE_STDOUT"
grep -Fq "missing_required_5162=0" "$PARAMETER_FILE_STDOUT"
grep -Fq "save_parameters=0" "$PARAMETER_FILE_STDOUT"
grep -Fq "saved_has_5161=1" "$PARAMETER_FILE_STDOUT"
grep -Fq "saved_has_5162=1" "$PARAMETER_FILE_STDOUT"
grep -Fq "saved_has_5221=1" "$PARAMETER_FILE_STDOUT"
grep -Fq "saved_has_5399=1" "$PARAMETER_FILE_STDOUT"
grep -Fq "saved_has_named_param=0" "$PARAMETER_FILE_STDOUT"
grep -Fq "backup_has_original_5161=1" "$PARAMETER_FILE_STDOUT"

INIT_HARNESS_STDOUT="$BUILD_DIR/linuxcnc_interp_init_harness.run.stdout.log"
grep -Fq "init=0" "$INIT_HARNESS_STDOUT"
grep -Fq "setup.length_units=2" "$INIT_HARNESS_STDOUT"
grep -Fq "setup.origin_index=1" "$INIT_HARNESS_STDOUT"
grep -Fq "setup.distance_mode=0" "$INIT_HARNESS_STDOUT"
grep -Fq "setup.feed_mode=0" "$INIT_HARNESS_STDOUT"
grep -Fq "setup.motion_mode=800" "$INIT_HARNESS_STDOUT"
grep -Fq "canon_event=INIT_CANON" "$INIT_HARNESS_STDOUT"
grep -Fq "canon_event=USE_LENGTH_UNITS units=2" "$INIT_HARNESS_STDOUT"
grep -Fq "canon_event=SET_G5X_OFFSET index=1" "$INIT_HARNESS_STDOUT"
grep -Fq "canon_event=SET_G92_OFFSET x=0 y=0 z=0" "$INIT_HARNESS_STDOUT"
grep -Fq "canon_event=SET_XY_ROTATION rotation=0" "$INIT_HARNESS_STDOUT"
grep -Fq "canon_event=SET_FEED_REFERENCE reference=2" "$INIT_HARNESS_STDOUT"
grep -Fq "inch_init=0" "$INIT_HARNESS_STDOUT"
grep -Fq "inch_setup.length_units=1" "$INIT_HARNESS_STDOUT"
grep -Fq "inch_external_length_units=0.0393701" "$INIT_HARNESS_STDOUT"
grep -Fq "inch_canon_event=USE_LENGTH_UNITS units=1" "$INIT_HARNESS_STDOUT"
grep -Fq "synch=0" "$INIT_HARNESS_STDOUT"
grep -Fq "synch.current_pocket=2" "$INIT_HARNESS_STDOUT"
grep -Fq "synch.selected_pocket=2" "$INIT_HARNESS_STDOUT"
grep -Fq "synch.tool_0=2" "$INIT_HARNESS_STDOUT"
grep -Fq "synch.tool_2=2" "$INIT_HARNESS_STDOUT"

INDEXER_HARNESS_STDOUT="$BUILD_DIR/linuxcnc_indexer_harness.run.stdout.log"
grep -Fq "execute=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "post_a=90" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=SET_MOTION_CONTROL_MODE mode=2 tolerance=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=UNLOCK_ROTARY line=1 joint=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=1 x=0 y=0 z=0 a=90 b=0 c=0 u=0 v=0 w=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=LOCK_ROTARY line=1 joint=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=SET_MOTION_CONTROL_MODE mode=1 tolerance=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=SET_NAIVECAM_TOLERANCE tolerance=0" "$INDEXER_HARNESS_STDOUT"
grep -Fq "canon_event=UPDATE_TAG line=1" "$INDEXER_HARNESS_STDOUT"

TP_API_STDOUT="$BUILD_DIR/linuxcnc_tp_api_probe.run.stdout.log"
grep -Fq "tp_default_queue_size=32" "$TP_API_STDOUT"
grep -Fq "tp_err_ok=0" "$TP_API_STDOUT"
grep -Fq "tc_linear=1" "$TP_API_STDOUT"
grep -Fq "tc_circular=2" "$TP_API_STDOUT"
grep -Fq "pose_xyz=1,2,3" "$TP_API_STDOUT"
grep -Fq "tp_create=0" "$TP_API_STDOUT"
grep -Fq "tp_set_cycle_time=0" "$TP_API_STDOUT"
grep -Fq "tp_add_line=0" "$TP_API_STDOUT"
grep -Fq "tp_done_after_line=1" "$TP_API_STDOUT"
grep -Fq "tp_final_pos=1,0,0" "$TP_API_STDOUT"
grep -Fq "tp_add_circle=0" "$TP_API_STDOUT"
grep -Fq "tp_done_after_arc=1" "$TP_API_STDOUT"
grep -Fq "tp_arc_final_pos_near_end=1" "$TP_API_STDOUT"
grep -Fq "tp_queue_add_line1=0" "$TP_API_STDOUT"
grep -Fq "tp_queue_depth_after_line1=1" "$TP_API_STDOUT"
grep -Fq "tp_queue_add_line2=0" "$TP_API_STDOUT"
grep -Fq "tp_queue_depth_after_line2=2" "$TP_API_STDOUT"
grep -Fq "tp_done_after_queued_lines=1" "$TP_API_STDOUT"
grep -Fq "tp_queue_final_depth=0" "$TP_API_STDOUT"
grep -Fq "tp_queue_final_pos_near_end=1" "$TP_API_STDOUT"

KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_kinematics_probe.run.stdout.log"
grep -Fq "kinematics_init=0" "$KINEMATICS_STDOUT"
grep -Fq "kinematics_type=1" "$KINEMATICS_STDOUT"
grep -Fq "kinematics_switchable=0" "$KINEMATICS_STDOUT"
grep -Fq "kinematics_switch=0" "$KINEMATICS_STDOUT"
grep -Fq "kinematics_forward=0" "$KINEMATICS_STDOUT"
grep -Fq "kinematics_forward_xyz=1,2,3" "$KINEMATICS_STDOUT"
grep -Fq "kinematics_forward_abcuvw=4,5,6,7,8,9" "$KINEMATICS_STDOUT"
grep -Fq "kinematics_inverse=0" "$KINEMATICS_STDOUT"
grep -Fq "kinematics_inverse_xyz=10,20,30" "$KINEMATICS_STDOUT"
grep -Fq "kinematics_inverse_abcuvw=40,50,60,70,80,90" "$KINEMATICS_STDOUT"

FIVEAXIS_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_5axis_kinematics_probe.run.stdout.log"
grep -Fq "fiveaxis_init=0" "$FIVEAXIS_KINEMATICS_STDOUT"
grep -Fq "fiveaxis_type=4" "$FIVEAXIS_KINEMATICS_STDOUT"
grep -Fq "fiveaxis_switchable=1" "$FIVEAXIS_KINEMATICS_STDOUT"
grep -Fq "fiveaxis_forward=0" "$FIVEAXIS_KINEMATICS_STDOUT"
grep -Fq "fiveaxis_inverse=0" "$FIVEAXIS_KINEMATICS_STDOUT"
grep -Fq "fiveaxis_roundtrip_joints=1" "$FIVEAXIS_KINEMATICS_STDOUT"
grep -Fq "fiveaxis_switch_identity=0" "$FIVEAXIS_KINEMATICS_STDOUT"
grep -Fq "fiveaxis_identity_forward=0" "$FIVEAXIS_KINEMATICS_STDOUT"
grep -Fq "fiveaxis_identity_near=1" "$FIVEAXIS_KINEMATICS_STDOUT"

XYZAC_TRT_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_xyzac_trt_kinematics_probe.run.stdout.log"
grep -Fq "xyzac_trt_init=0" "$XYZAC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzac_trt_type=4" "$XYZAC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzac_trt_switchable=1" "$XYZAC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzac_trt_forward=0" "$XYZAC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzac_trt_inverse=0" "$XYZAC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzac_trt_roundtrip_joints=1" "$XYZAC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzac_trt_switch_identity=0" "$XYZAC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzac_trt_identity_forward=0" "$XYZAC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzac_trt_identity_near=1" "$XYZAC_TRT_KINEMATICS_STDOUT"

XYZBC_TRT_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_xyzbc_trt_kinematics_probe.run.stdout.log"
grep -Fq "xyzbc_trt_init=0" "$XYZBC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzbc_trt_type=4" "$XYZBC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzbc_trt_switchable=1" "$XYZBC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzbc_trt_forward=0" "$XYZBC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzbc_trt_inverse=0" "$XYZBC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzbc_trt_roundtrip_joints=1" "$XYZBC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzbc_trt_switch_identity=0" "$XYZBC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzbc_trt_identity_forward=0" "$XYZBC_TRT_KINEMATICS_STDOUT"
grep -Fq "xyzbc_trt_identity_near=1" "$XYZBC_TRT_KINEMATICS_STDOUT"

COREXY_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_corexy_kinematics_probe.run.stdout.log"
grep -Fq "corexy_init=0" "$COREXY_KINEMATICS_STDOUT"
grep -Fq "corexy_type=4" "$COREXY_KINEMATICS_STDOUT"
grep -Fq "corexy_switchable=0" "$COREXY_KINEMATICS_STDOUT"
grep -Fq "corexy_forward=0" "$COREXY_KINEMATICS_STDOUT"
grep -Fq "corexy_inverse=0" "$COREXY_KINEMATICS_STDOUT"
grep -Fq "corexy_roundtrip_joints=1" "$COREXY_KINEMATICS_STDOUT"

ROTATE_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_rotate_kinematics_probe.run.stdout.log"
grep -Fq "rotate_init=0" "$ROTATE_KINEMATICS_STDOUT"
grep -Fq "rotate_type=4" "$ROTATE_KINEMATICS_STDOUT"
grep -Fq "rotate_switchable=0" "$ROTATE_KINEMATICS_STDOUT"
grep -Fq "rotate_forward=0" "$ROTATE_KINEMATICS_STDOUT"
grep -Fq "rotate_inverse=0" "$ROTATE_KINEMATICS_STDOUT"
grep -Fq "rotate_roundtrip_joints=1" "$ROTATE_KINEMATICS_STDOUT"

ROSE_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_rose_kinematics_probe.run.stdout.log"
grep -Fq "rose_init=0" "$ROSE_KINEMATICS_STDOUT"
grep -Fq "rose_type=4" "$ROSE_KINEMATICS_STDOUT"
grep -Fq "rose_switchable=0" "$ROSE_KINEMATICS_STDOUT"
grep -Fq "rose_forward=0" "$ROSE_KINEMATICS_STDOUT"
grep -Fq "rose_inverse=0" "$ROSE_KINEMATICS_STDOUT"
grep -Fq "rose_roundtrip_joints=1" "$ROSE_KINEMATICS_STDOUT"

MAX_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_max_kinematics_probe.run.stdout.log"
grep -Fq "max_init=0" "$MAX_KINEMATICS_STDOUT"
grep -Fq "max_type=4" "$MAX_KINEMATICS_STDOUT"
grep -Fq "max_switchable=0" "$MAX_KINEMATICS_STDOUT"
grep -Fq "max_forward=0" "$MAX_KINEMATICS_STDOUT"
grep -Fq "max_inverse=0" "$MAX_KINEMATICS_STDOUT"
grep -Fq "max_roundtrip_joints=1" "$MAX_KINEMATICS_STDOUT"

LINEARDELTA_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_lineardelta_kinematics_probe.run.stdout.log"
grep -Fq "lineardelta_init=0" "$LINEARDELTA_KINEMATICS_STDOUT"
grep -Fq "lineardelta_type=4" "$LINEARDELTA_KINEMATICS_STDOUT"
grep -Fq "lineardelta_switchable=0" "$LINEARDELTA_KINEMATICS_STDOUT"
grep -Fq "lineardelta_inverse=0" "$LINEARDELTA_KINEMATICS_STDOUT"
grep -Fq "lineardelta_forward=0" "$LINEARDELTA_KINEMATICS_STDOUT"
grep -Fq "lineardelta_roundtrip_pose=1" "$LINEARDELTA_KINEMATICS_STDOUT"

ROTARYDELTA_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_rotarydelta_kinematics_probe.run.stdout.log"
grep -Fq "rotarydelta_init=0" "$ROTARYDELTA_KINEMATICS_STDOUT"
grep -Fq "rotarydelta_type=4" "$ROTARYDELTA_KINEMATICS_STDOUT"
grep -Fq "rotarydelta_switchable=0" "$ROTARYDELTA_KINEMATICS_STDOUT"
grep -Fq "rotarydelta_inverse=0" "$ROTARYDELTA_KINEMATICS_STDOUT"
grep -Fq "rotarydelta_forward=0" "$ROTARYDELTA_KINEMATICS_STDOUT"
grep -Fq "rotarydelta_roundtrip_pose=1" "$ROTARYDELTA_KINEMATICS_STDOUT"

SCORBOT_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_scorbot_kinematics_probe.run.stdout.log"
grep -Fq "scorbot_init=0" "$SCORBOT_KINEMATICS_STDOUT"
grep -Fq "scorbot_type=4" "$SCORBOT_KINEMATICS_STDOUT"
grep -Fq "scorbot_switchable=0" "$SCORBOT_KINEMATICS_STDOUT"
grep -Fq "scorbot_forward=0" "$SCORBOT_KINEMATICS_STDOUT"
grep -Fq "scorbot_inverse=0" "$SCORBOT_KINEMATICS_STDOUT"
grep -Fq "scorbot_roundtrip_forward=0" "$SCORBOT_KINEMATICS_STDOUT"
grep -Fq "scorbot_roundtrip_pose=1" "$SCORBOT_KINEMATICS_STDOUT"

TRIPOD_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_tripod_kinematics_probe.run.stdout.log"
grep -Fq "tripod_init=0" "$TRIPOD_KINEMATICS_STDOUT"
grep -Fq "tripod_type=4" "$TRIPOD_KINEMATICS_STDOUT"
grep -Fq "tripod_switchable=0" "$TRIPOD_KINEMATICS_STDOUT"
grep -Fq "tripod_inverse=0" "$TRIPOD_KINEMATICS_STDOUT"
grep -Fq "tripod_inverse_flags=0" "$TRIPOD_KINEMATICS_STDOUT"
grep -Fq "tripod_forward=0" "$TRIPOD_KINEMATICS_STDOUT"
grep -Fq "tripod_roundtrip_xyz=1" "$TRIPOD_KINEMATICS_STDOUT"
grep -Fq "tripod_inverse_below=0" "$TRIPOD_KINEMATICS_STDOUT"
grep -Fq "tripod_inverse_below_flags=1" "$TRIPOD_KINEMATICS_STDOUT"
grep -Fq "tripod_forward_below=0" "$TRIPOD_KINEMATICS_STDOUT"
grep -Fq "tripod_roundtrip_below_xyz=1" "$TRIPOD_KINEMATICS_STDOUT"

SCARA_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_scara_kinematics_probe.run.stdout.log"
grep -Fq "scara_init=0" "$SCARA_KINEMATICS_STDOUT"
grep -Fq "scara_type=4" "$SCARA_KINEMATICS_STDOUT"
grep -Fq "scara_switchable=1" "$SCARA_KINEMATICS_STDOUT"
grep -Fq "scara_forward=0" "$SCARA_KINEMATICS_STDOUT"
grep -Fq "scara_forward_iflags=0" "$SCARA_KINEMATICS_STDOUT"
grep -Fq "scara_inverse=0" "$SCARA_KINEMATICS_STDOUT"
grep -Fq "scara_roundtrip_joints=1" "$SCARA_KINEMATICS_STDOUT"
grep -Fq "scara_switch_identity=0" "$SCARA_KINEMATICS_STDOUT"
grep -Fq "scara_identity_forward=0" "$SCARA_KINEMATICS_STDOUT"
grep -Fq "scara_identity_near=1" "$SCARA_KINEMATICS_STDOUT"

PUMA_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_puma_kinematics_probe.run.stdout.log"
grep -Fq "puma_init=0" "$PUMA_KINEMATICS_STDOUT"
grep -Fq "puma_type=4" "$PUMA_KINEMATICS_STDOUT"
grep -Fq "puma_switchable=1" "$PUMA_KINEMATICS_STDOUT"
grep -Fq "puma_forward=0" "$PUMA_KINEMATICS_STDOUT"
grep -Fq "puma_inverse=0" "$PUMA_KINEMATICS_STDOUT"
grep -Fq "puma_roundtrip_forward=0" "$PUMA_KINEMATICS_STDOUT"
grep -Fq "puma_roundtrip_pose=1" "$PUMA_KINEMATICS_STDOUT"
grep -Fq "puma_switch_identity=0" "$PUMA_KINEMATICS_STDOUT"
grep -Fq "puma_identity_forward=0" "$PUMA_KINEMATICS_STDOUT"
grep -Fq "puma_identity_near=1" "$PUMA_KINEMATICS_STDOUT"

GENSER_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_genser_kinematics_probe.run.stdout.log"
grep -Fq "genser_init=0" "$GENSER_KINEMATICS_STDOUT"
grep -Fq "genser_type=4" "$GENSER_KINEMATICS_STDOUT"
grep -Fq "genser_switchable=1" "$GENSER_KINEMATICS_STDOUT"
grep -Fq "genser_forward=0" "$GENSER_KINEMATICS_STDOUT"
grep -Fq "genser_inverse=0" "$GENSER_KINEMATICS_STDOUT"
grep -Fq "genser_roundtrip_joints=1" "$GENSER_KINEMATICS_STDOUT"
grep -Fq "genser_roundtrip_uvw=1" "$GENSER_KINEMATICS_STDOUT"
grep -Fq "genser_switch_identity=0" "$GENSER_KINEMATICS_STDOUT"
grep -Fq "genser_identity_forward=0" "$GENSER_KINEMATICS_STDOUT"
grep -Fq "genser_identity_near=1" "$GENSER_KINEMATICS_STDOUT"

GENHEX_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_genhex_kinematics_probe.run.stdout.log"
grep -Fq "genhex_init=0" "$GENHEX_KINEMATICS_STDOUT"
grep -Fq "genhex_type=4" "$GENHEX_KINEMATICS_STDOUT"
grep -Fq "genhex_switchable=1" "$GENHEX_KINEMATICS_STDOUT"
grep -Fq "genhex_inverse=0" "$GENHEX_KINEMATICS_STDOUT"
grep -Fq "genhex_forward_warmup=-1" "$GENHEX_KINEMATICS_STDOUT"
grep -Fq "genhex_forward=0" "$GENHEX_KINEMATICS_STDOUT"
grep -Fq "genhex_roundtrip_pose=1" "$GENHEX_KINEMATICS_STDOUT"
grep -Fq "genhex_switch_identity=0" "$GENHEX_KINEMATICS_STDOUT"
grep -Fq "genhex_identity_forward=0" "$GENHEX_KINEMATICS_STDOUT"
grep -Fq "genhex_identity_near=1" "$GENHEX_KINEMATICS_STDOUT"

PENTAKINS_KINEMATICS_STDOUT="$BUILD_DIR/linuxcnc_pentakins_kinematics_probe.run.stdout.log"
grep -Fq "pentakins_init=0" "$PENTAKINS_KINEMATICS_STDOUT"
grep -Fq "pentakins_type=4" "$PENTAKINS_KINEMATICS_STDOUT"
grep -Fq "pentakins_switchable=0" "$PENTAKINS_KINEMATICS_STDOUT"
grep -Fq "pentakins_inverse=0" "$PENTAKINS_KINEMATICS_STDOUT"
grep -Fq "pentakins_positive_struts=1" "$PENTAKINS_KINEMATICS_STDOUT"
grep -Fq "pentakins_forward=0" "$PENTAKINS_KINEMATICS_STDOUT"
grep -Fq "pentakins_roundtrip_pose=1" "$PENTAKINS_KINEMATICS_STDOUT"

check_fixture_output() {
  local fixture="$1"
  local expected="$2"
  local stdout_file="$3"
  local require_mdi="${4:-1}"

  if [[ "$require_mdi" == "1" ]]; then
    grep -Fq "read=0" "$stdout_file"
    grep -Fq "parse_line=0" "$stdout_file"

    local line_count
    line_count="$(grep -cv '^[[:space:]]*$' "$fixture")"
    local line_number
    for ((line_number = 1; line_number <= line_count; line_number += 1)); do
      if ! grep -Fq "execute_line_${line_number}=0" "$stdout_file" &&
         ! grep -Fq "execute_line_${line_number}=1" "$stdout_file" &&
         ! grep -Fq "execute_line_${line_number}=2" "$stdout_file"; then
        echo "missing successful execute status for line $line_number in $stdout_file" >&2
        exit 1
      fi
    done
  else
    grep -Fq "file_open=0" "$stdout_file"
    grep -Fq "file_execute_1=0" "$stdout_file"
  fi

  while IFS= read -r expected_event; do
    [[ -z "$expected_event" ]] && continue
    grep -Fq "$expected_event" "$stdout_file"
  done < "$expected"
}

RUN_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.run.stdout.log"
check_fixture_output \
  "$GCODE_FIXTURE_DIR/minimal_linear.ngc" \
  "$CANON_FIXTURE_DIR/minimal_linear.events" \
  "$RUN_STDOUT"

DO_WHILE_BREAK_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.do_while_break.run.stdout.log"
grep -Fq "file_open=0" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "file_read_count=30" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "file_execute_count=30" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=MESSAGE: must-execute-outer" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=MESSAGE: must-execute-nested" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=MESSAGE: must-execute-inner" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=MESSAGE: post-inner-while" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=MESSAGE: post-nested-while" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=MESSAGE: post-outer-while" "$DO_WHILE_BREAK_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$DO_WHILE_BREAK_STDOUT"
if grep -Fq "canon_event=MESSAGE: do-not-execute" "$DO_WHILE_BREAK_STDOUT"; then
  echo "unexpected do-while-break skipped branch event" >&2
  exit 1
fi

OWORD_BUG315_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.oword_bug315.run.stdout.log"
grep -Fq "file_open=0" "$OWORD_BUG315_STDOUT"
grep -Fq "file_read_count=23" "$OWORD_BUG315_STDOUT"
grep -Fq "file_execute_count=23" "$OWORD_BUG315_STDOUT"
grep -Fq "canon_event=MESSAGE: running sub" "$OWORD_BUG315_STDOUT"
grep -Fq "canon_event=MESSAGE: breaking out of sub" "$OWORD_BUG315_STDOUT"
grep -Fq "canon_event=MESSAGE: done" "$OWORD_BUG315_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$OWORD_BUG315_STDOUT"

OWORD_BUG315_P2_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.oword_bug315_p2.run.stdout.log"
grep -Fq "file_open=0" "$OWORD_BUG315_P2_STDOUT"
grep -Fq "file_read_count=26" "$OWORD_BUG315_P2_STDOUT"
grep -Fq "file_execute_count=26" "$OWORD_BUG315_P2_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=7 x=2 y=2 z=2" "$OWORD_BUG315_P2_STDOUT"
grep -Fq "canon_event=MESSAGE: executing this one" "$OWORD_BUG315_P2_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$OWORD_BUG315_P2_STDOUT"
if grep -Fq "canon_event=MESSAGE: not executing this one" "$OWORD_BUG315_P2_STDOUT"; then
  echo "unexpected oword-bug315-p2 skipped subroutine event" >&2
  exit 1
fi

INTERP_EXISTS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.interp_exists.run.stdout.log"
grep -Fq "file_open=0" "$INTERP_EXISTS_STDOUT"
grep -Fq "file_read_count=7" "$INTERP_EXISTS_STDOUT"
grep -Fq "file_execute_count=7" "$INTERP_EXISTS_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=2 x=1 y=0 z=1" "$INTERP_EXISTS_STDOUT"
grep -Fq "canon_event=COMMENT: comment" "$INTERP_EXISTS_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=6 x=1 y=0 z=0" "$INTERP_EXISTS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$INTERP_EXISTS_STDOUT"

SIM_AXIS_FOAM_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.sim_axis_foam.run.stdout.log"
grep -Fq "file_open=0" "$SIM_AXIS_FOAM_STDOUT"
grep -Fq "file_read_count=23" "$SIM_AXIS_FOAM_STDOUT"
if grep -Fq "Bad character 'u' used" "$SIM_AXIS_FOAM_STDOUT"; then
  echo "axis_foam INI did not enable U/V axis readers" >&2
  exit 1
fi

SIM_BRIDGEMILL_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.sim_bridgemill.run.stdout.log"
grep -Fq "file_open=0" "$SIM_BRIDGEMILL_STDOUT"
if grep -Fq "Bad character 'w' used" "$SIM_BRIDGEMILL_STDOUT"; then
  echo "bridgemill INI did not enable W axis reader" >&2
  exit 1
fi

SIM_GEOMETRY_XYZC_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.sim_geometry_xyzc.run.stdout.log"
grep -Fq "file_open=0" "$SIM_GEOMETRY_XYZC_STDOUT"
grep -Fq "canon_event=USER_M_COMMAND code=M110" "$SIM_GEOMETRY_XYZC_STDOUT"
if grep -Fq "Unknown m code used: M110" "$SIM_GEOMETRY_XYZC_STDOUT"; then
  echo "xyzc INI did not register USER_M_PATH M110" >&2
  exit 1
fi

SIM_EXTERNAL_OFFSETS_DYN_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.sim_external_offsets_dyn.run.stdout.log"
grep -Fq "file_open=0" "$SIM_EXTERNAL_OFFSETS_DYN_STDOUT"
grep -Fq "canon_event=USER_M_COMMAND code=M111" "$SIM_EXTERNAL_OFFSETS_DYN_STDOUT"
if grep -Fq "Unknown m code used: M111" "$SIM_EXTERNAL_OFFSETS_DYN_STDOUT"; then
  echo "external_offsets INI did not register USER_M_PATH M111" >&2
  exit 1
fi

RETURN_VALUE_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.return_value.run.stdout.log"
grep -Fq "file_open=0" "$RETURN_VALUE_STDOUT"
grep -Fq "file_read_count=75" "$RETURN_VALUE_STDOUT"
grep -Fq "file_execute_count=75" "$RETURN_VALUE_STDOUT"
grep -Fq "canon_event=MESSAGE: line 6.000000: _value: - expected 0.000000, got 0.000000" "$RETURN_VALUE_STDOUT"
grep -Fq "canon_event=MESSAGE: line 28.000000: call with arg1=2.000000 expect 246.000000, got 246.000000" "$RETURN_VALUE_STDOUT"
grep -Fq "canon_event=MESSAGE: line 37.000000: call with arg1=-1.000000 expect 0.000000, got 0.000000" "$RETURN_VALUE_STDOUT"
grep -Fq "canon_event=MESSAGE: line 47.000000: call with arg1=0.000000 expect 4712.000000, got 4712.000000" "$RETURN_VALUE_STDOUT"
grep -Fq "canon_event=MESSAGE: line 53.000000: _value=4712.000000 - expected 4712.000000" "$RETURN_VALUE_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$RETURN_VALUE_STDOUT"
if grep -Fq "fail:" "$RETURN_VALUE_STDOUT"; then
  echo "unexpected return-value failure message" >&2
  exit 1
fi

SUBS_FOLLOW_MAIN_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.subs_follow_main.run.stdout.log"
grep -Fq "file_open=0" "$SUBS_FOLLOW_MAIN_STDOUT"
grep -Fq "file_read_count=9" "$SUBS_FOLLOW_MAIN_STDOUT"
grep -Fq "file_execute_count=9" "$SUBS_FOLLOW_MAIN_STDOUT"
grep -Fq "canon_event=COMMENT: Subs may follow main program " "$SUBS_FOLLOW_MAIN_STDOUT"
grep -Fq "canon_event=PALLET_SHUTTLE" "$SUBS_FOLLOW_MAIN_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$SUBS_FOLLOW_MAIN_STDOUT"

FRACTIONAL_LINENUMBERS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.fractional_linenumbers.run.stdout.log"
grep -Fq "file_open=0" "$FRACTIONAL_LINENUMBERS_STDOUT"
grep -Fq "file_read_count=4" "$FRACTIONAL_LINENUMBERS_STDOUT"
grep -Fq "file_execute_count=4" "$FRACTIONAL_LINENUMBERS_STDOUT"
grep -Fq "canon_event=SET_SPINDLE_SPEED spindle=0 speed=300" "$FRACTIONAL_LINENUMBERS_STDOUT"
grep -Fq "canon_event=SET_SPINDLE_SPEED spindle=0 speed=600" "$FRACTIONAL_LINENUMBERS_STDOUT"
grep -Fq "canon_event=SET_SPINDLE_SPEED spindle=0 speed=1200" "$FRACTIONAL_LINENUMBERS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$FRACTIONAL_LINENUMBERS_STDOUT"

CAM_NISLEY_BARE_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.cam_nisley_bare.run.stdout.log"
grep -Fq "file_open=0" "$CAM_NISLEY_BARE_STDOUT"
grep -Fq "file_read_count=57" "$CAM_NISLEY_BARE_STDOUT"
grep -Fq "file_execute_count=57" "$CAM_NISLEY_BARE_STDOUT"
grep -Fq "file_saw_error=1" "$CAM_NISLEY_BARE_STDOUT"
grep -Fq "file_error_text=Requested tool 1 not found in the tool table" "$CAM_NISLEY_BARE_STDOUT"

CAM_NISLEY_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.cam_nisley.run.stdout.log"
grep -Fq "file_open=0" "$CAM_NISLEY_STDOUT"
grep -Fq "file_read_count=1584" "$CAM_NISLEY_STDOUT"
grep -Fq "file_execute_count=1584" "$CAM_NISLEY_STDOUT"
grep -Fq "file_saw_error=0" "$CAM_NISLEY_STDOUT"
grep -Fq "error_text=Bad character '%' used" "$CAM_NISLEY_STDOUT"
grep -Fq "canon_event=COMMENT: Ed Nisley - Nov 2006 - Mar 2007" "$CAM_NISLEY_STDOUT"
grep -Fq "canon_event=SELECT_TOOL tool=1" "$CAM_NISLEY_STDOUT"
grep -Fq "canon_event=CHANGE_TOOL" "$CAM_NISLEY_STDOUT"
grep -Fq "canon_event=START_SPINDLE_CLOCKWISE spindle=0" "$CAM_NISLEY_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation on right" "$CAM_NISLEY_STDOUT"
grep -Fq "canon_event=ARC_FEED line=223 first_end=1.91351e-18 second_end=-15.1438" "$CAM_NISLEY_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=241 x=75 y=0 z=75" "$CAM_NISLEY_STDOUT"
grep -Fq "canon_event=MESSAGE: Done!" "$CAM_NISLEY_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$CAM_NISLEY_STDOUT"
if grep -Fq "file_error_text=Requested tool 1 not found in the tool table" "$CAM_NISLEY_STDOUT"; then
  echo "cam-nisley with upstream tool table unexpectedly failed tool lookup" >&2
  exit 1
fi

CRAZY_PATHS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.crazy_paths.run.stdout.log"
grep -Fq "file_open=0" "$CRAZY_PATHS_STDOUT"
grep -Fq "file_read_count=1526" "$CRAZY_PATHS_STDOUT"
grep -Fq "file_execute_count=1526" "$CRAZY_PATHS_STDOUT"
grep -Fq "file_saw_error=0" "$CRAZY_PATHS_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation on right" "$CRAZY_PATHS_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation on left" "$CRAZY_PATHS_STDOUT"
grep -Fq "canon_event=ARC_FEED line=8 first_end=-99.2047 second_end=-102.892 first_axis=-98.9043 second_axis=-102.097 rotation=-1 axis_end_point=0" "$CRAZY_PATHS_STDOUT"
grep -Fq "canon_event=ARC_FEED line=103 first_end=-40.3437 second_end=-103.278 first_axis=-45.0196 second_axis=-102.442 rotation=-1 axis_end_point=0" "$CRAZY_PATHS_STDOUT"
grep -Fq "canon_event=STRAIGHT_FEED line=260 x=3.58919 y=30.3694 z=0" "$CRAZY_PATHS_STDOUT"
grep -Fq "canon_event=ARC_FEED line=445 first_end=101.539 second_end=44.5843 first_axis=100.896 second_axis=46.212 rotation=1 axis_end_point=0" "$CRAZY_PATHS_STDOUT"
grep -Fq "canon_event=STRAIGHT_FEED line=469 x=98.8154 y=102.176 z=0" "$CRAZY_PATHS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$CRAZY_PATHS_STDOUT"

NAMEDPARAM_BUG424_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.namedparam_bug424.run.stdout.log"
grep -Fq "file_open=0" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "file_read_count=6" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "file_execute_count=6" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "setup.current_x=2" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "setup.current_y=3" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "setup.current_z=4" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "canon_event=USE_LENGTH_UNITS units=1" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=5 x=2 y=3 z=4" "$NAMEDPARAM_BUG424_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$NAMEDPARAM_BUG424_STDOUT"

FLOWSNAKE_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.flowsnake.run.stdout.log"
grep -Fq "file_open=0" "$FLOWSNAKE_STDOUT"
grep -Fq "file_read_count=6414" "$FLOWSNAKE_STDOUT"
grep -Fq "file_execute_count=6414" "$FLOWSNAKE_STDOUT"
grep -Fq "file_saw_error=0" "$FLOWSNAKE_STDOUT"
grep -Fq "canon_event=COMMENT: Program to mill a flowsnake" "$FLOWSNAKE_STDOUT"
grep -Fq "canon_event=START_SPINDLE_CLOCKWISE spindle=0" "$FLOWSNAKE_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=33 x=0.25 y=1 z=1" "$FLOWSNAKE_STDOUT"
grep -Fq "canon_event=STRAIGHT_FEED line=13 x=3.75 y=1 z=0" "$FLOWSNAKE_STDOUT"
grep -Fq "canon_event=STRAIGHT_FEED line=13 x=2 y=3.95 z=0" "$FLOWSNAKE_STDOUT"
grep -Fq "canon_event=STRAIGHT_FEED line=13 x=0.25 y=1 z=0" "$FLOWSNAKE_STDOUT"
grep -Fq "canon_event=STOP_SPINDLE_TURNING spindle=0" "$FLOWSNAKE_STDOUT"
grep -Fq "setup.current_x=0.25" "$FLOWSNAKE_STDOUT"
grep -Fq "setup.current_y=1" "$FLOWSNAKE_STDOUT"
grep -Fq "setup.current_z=1" "$FLOWSNAKE_STDOUT"

INSIDE_CORNERS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.inside_corners.run.stdout.log"
grep -Fq "file_open=0" "$INSIDE_CORNERS_STDOUT"
grep -Fq "file_read_count=397" "$INSIDE_CORNERS_STDOUT"
grep -Fq "file_execute_count=397" "$INSIDE_CORNERS_STDOUT"
grep -Fq "file_saw_error=0" "$INSIDE_CORNERS_STDOUT"
grep -Fq "canon_event=PROGRAM_STOP" "$INSIDE_CORNERS_STDOUT"
grep -Fq "canon_event=DWELL seconds=3" "$INSIDE_CORNERS_STDOUT"
grep -Fq "canon_event=SELECT_PLANE plane=1" "$INSIDE_CORNERS_STDOUT"
grep -Fq "canon_event=SELECT_PLANE plane=3" "$INSIDE_CORNERS_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation on left" "$INSIDE_CORNERS_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation on right" "$INSIDE_CORNERS_STDOUT"
grep -Fq "canon_event=ARC_FEED line=16 first_end=1.2281 second_end=1.3456 first_axis=0.628 second_axis=1.0458 rotation=1 axis_end_point=-0.1" "$INSIDE_CORNERS_STDOUT"
grep -Fq "canon_event=STRAIGHT_FEED line=14 x=0.701154 y=0.162499 z=-0.1" "$INSIDE_CORNERS_STDOUT"
grep -Fq "canon_event=ARC_FEED line=74 first_end=0.298564 second_end=1.82077 first_axis=0.3459 second_axis=1.7626 rotation=1 axis_end_point=0" "$INSIDE_CORNERS_STDOUT"
grep -Fq "canon_event=STRAIGHT_FEED line=107 x=0 y=0 z=1" "$INSIDE_CORNERS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$INSIDE_CORNERS_STDOUT"

CCOMP_LATHE_COMP_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.ccomp_lathe_comp.run.stdout.log"
grep -Fq "file_open=0" "$CCOMP_LATHE_COMP_STDOUT"
grep -Fq "file_read_count=49" "$CCOMP_LATHE_COMP_STDOUT"
grep -Fq "file_execute_count=49" "$CCOMP_LATHE_COMP_STDOUT"
grep -Fq "file_saw_error=0" "$CCOMP_LATHE_COMP_STDOUT"
grep -Fq "canon_event=SELECT_PLANE plane=3" "$CCOMP_LATHE_COMP_STDOUT"
grep -Fq "canon_event=SELECT_TOOL tool=2" "$CCOMP_LATHE_COMP_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation on left" "$CCOMP_LATHE_COMP_STDOUT"
grep -Fq "canon_event=SELECT_TOOL tool=7" "$CCOMP_LATHE_COMP_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation on right" "$CCOMP_LATHE_COMP_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$CCOMP_LATHE_COMP_STDOUT"

CCOMP_MILL_G90G91G92_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.ccomp_mill_g90g91g92.run.stdout.log"
grep -Fq "file_open=0" "$CCOMP_MILL_G90G91G92_STDOUT"
grep -Fq "file_read_count=28" "$CCOMP_MILL_G90G91G92_STDOUT"
grep -Fq "file_execute_count=28" "$CCOMP_MILL_G90G91G92_STDOUT"
grep -Fq "file_saw_error=0" "$CCOMP_MILL_G90G91G92_STDOUT"
grep -Fq "canon_event=SELECT_TOOL tool=3" "$CCOMP_MILL_G90G91G92_STDOUT"
grep -Fq "canon_event=SET_G92_OFFSET x=0 y=0 z=0.5" "$CCOMP_MILL_G90G91G92_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation on left" "$CCOMP_MILL_G90G91G92_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation off" "$CCOMP_MILL_G90G91G92_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$CCOMP_MILL_G90G91G92_STDOUT"

CCOMP_MILL_LINE_ARC_ENTRY_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.ccomp_mill_line_arc_entry.run.stdout.log"
grep -Fq "file_open=0" "$CCOMP_MILL_LINE_ARC_ENTRY_STDOUT"
grep -Fq "file_read_count=33" "$CCOMP_MILL_LINE_ARC_ENTRY_STDOUT"
grep -Fq "file_execute_count=33" "$CCOMP_MILL_LINE_ARC_ENTRY_STDOUT"
grep -Fq "file_saw_error=0" "$CCOMP_MILL_LINE_ARC_ENTRY_STDOUT"
grep -Fq "canon_event=SELECT_TOOL tool=4" "$CCOMP_MILL_LINE_ARC_ENTRY_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation on left" "$CCOMP_MILL_LINE_ARC_ENTRY_STDOUT"
grep -Fq "canon_event=ARC_FEED line=23 first_end=2 second_end=3.01969" "$CCOMP_MILL_LINE_ARC_ENTRY_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation off" "$CCOMP_MILL_LINE_ARC_ENTRY_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$CCOMP_MILL_LINE_ARC_ENTRY_STDOUT"

CCOMP_MILL_ZCHANGES_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.ccomp_mill_zchanges.run.stdout.log"
grep -Fq "file_open=0" "$CCOMP_MILL_ZCHANGES_STDOUT"
grep -Fq "file_read_count=81" "$CCOMP_MILL_ZCHANGES_STDOUT"
grep -Fq "file_execute_count=81" "$CCOMP_MILL_ZCHANGES_STDOUT"
grep -Fq "file_saw_error=0" "$CCOMP_MILL_ZCHANGES_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation on left" "$CCOMP_MILL_ZCHANGES_STDOUT"
grep -Fq "canon_event=ARC_FEED line=15 first_end=0.5 second_end=0.752461" "$CCOMP_MILL_ZCHANGES_STDOUT"
grep -Fq "canon_event=STRAIGHT_FEED line=54 x=0.49826 y=-0.24826 z=0" "$CCOMP_MILL_ZCHANGES_STDOUT"
grep -Fq "canon_event=COMMENT: interpreter: cutter radius compensation off" "$CCOMP_MILL_ZCHANGES_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$CCOMP_MILL_ZCHANGES_STDOUT"

check_bad_file_fixture() {
  local name="$1"
  local read_count="$2"
  local execute_count="$3"
  local error_text="$4"
  local stdout_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.run.stdout.log"

  grep -Fq "file_open=0" "$stdout_file"
  grep -Fq "file_read_count=$read_count" "$stdout_file"
  grep -Fq "file_execute_count=$execute_count" "$stdout_file"
  grep -Fq "file_saw_error=1" "$stdout_file"
  grep -Fq "file_error_text=$error_text" "$stdout_file"
  grep -Fq "canon_event=ON_RESET" "$stdout_file"
}

check_bad_file_fixture bad_a_in_canned_cycle 3 2 "Cannot put an a in canned cycle"
check_bad_file_fixture bad_a_in_canned_cycle2 2 1 "Cannot put an a in canned cycle"
check_bad_file_fixture bad_ccomp_arcexit 7 7 "The move just after exiting cutter compensation mode must be straight, not an arc"
check_bad_file_fixture bad_ccomp_gouging 7 7 "Straight feed in concave corner cannot be reached by the tool without gouging"
check_bad_file_fixture bad_exists_1 2 1 "Expected # reading parameter"
check_bad_file_fixture bad_exists_2 2 1 "Expected ] reading bracketed parameter"
check_bad_file_fixture bad_exists_3 3 2 "Parameter number out of range"
check_bad_file_fixture bad_exists_4 2 1 "Unknown word starting with f"
check_bad_file_fixture bad_exists_5 2 1 "Named parameter not terminated"
check_bad_file_fixture bad_exists_6 2 1 "bad number format (conversion failed) parsing ''"
check_bad_file_fixture bad_exists_7 2 1 "Expected ] reading bracketed parameter"

BAD_NESTED_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.bad_nested.run.stdout.log"
grep -Fq "file_open=0" "$BAD_NESTED_STDOUT"
grep -Fq "file_read_count=6" "$BAD_NESTED_STDOUT"
grep -Fq "file_execute_count=5" "$BAD_NESTED_STDOUT"
grep -Fq "file_saw_error=1" "$BAD_NESTED_STDOUT"
grep -Fq "file_error_text=Nested subroutine definition" "$BAD_NESTED_STDOUT"
grep -Fq "canon_event=ON_RESET" "$BAD_NESTED_STDOUT"

BAD_NO_FEED_RATE_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.bad_no_feed_rate.run.stdout.log"
grep -Fq "file_open=0" "$BAD_NO_FEED_RATE_STDOUT"
grep -Fq "file_read_count=2" "$BAD_NO_FEED_RATE_STDOUT"
grep -Fq "file_execute_count=2" "$BAD_NO_FEED_RATE_STDOUT"
grep -Fq "file_saw_error=1" "$BAD_NO_FEED_RATE_STDOUT"
grep -Fq "file_error_text=Cannot do g1 with zero feed rate" "$BAD_NO_FEED_RATE_STDOUT"
grep -Fq "canon_event=ON_RESET" "$BAD_NO_FEED_RATE_STDOUT"

BAD_NO_IJR_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.bad_no_ijr.run.stdout.log"
grep -Fq "file_open=0" "$BAD_NO_IJR_STDOUT"
grep -Fq "file_read_count=2" "$BAD_NO_IJR_STDOUT"
grep -Fq "file_execute_count=2" "$BAD_NO_IJR_STDOUT"
grep -Fq "file_saw_error=1" "$BAD_NO_IJR_STDOUT"
grep -Fq "file_error_text=R i j k words all missing for arc" "$BAD_NO_IJR_STDOUT"
grep -Fq "canon_event=ON_RESET" "$BAD_NO_IJR_STDOUT"

BAD_PROBE_NO_AXES_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.bad_probe_no_axes.run.stdout.log"
grep -Fq "file_open=0" "$BAD_PROBE_NO_AXES_STDOUT"
grep -Fq "file_read_count=2" "$BAD_PROBE_NO_AXES_STDOUT"
grep -Fq "file_execute_count=1" "$BAD_PROBE_NO_AXES_STDOUT"
grep -Fq "file_saw_error=1" "$BAD_PROBE_NO_AXES_STDOUT"
grep -Fq "file_error_text=All axes missing with motion code" "$BAD_PROBE_NO_AXES_STDOUT"
grep -Fq "canon_event=ON_RESET" "$BAD_PROBE_NO_AXES_STDOUT"

G33_1_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.g33_1.run.stdout.log"
grep -Fq "file_open=0" "$G33_1_STDOUT"
grep -Fq "file_read_count=5" "$G33_1_STDOUT"
grep -Fq "file_execute_count=5" "$G33_1_STDOUT"
grep -Fq "file_saw_error=0" "$G33_1_STDOUT"
grep -Fq "run_step phase=execute step=4 rc=0 line=4" "$G33_1_STDOUT"
grep -Fq "statement_uri=g33.1%20z-1.2%20k0.1" "$G33_1_STDOUT"
grep -Fq "canon_event=START_SPINDLE_CLOCKWISE spindle=0" "$G33_1_STDOUT"
grep -Fq "canon_event=START_SPEED_FEED_SYNCH spindle=0 feed_per_revolution=0.1 velocity_mode=0" "$G33_1_STDOUT"
grep -Fq "canon_event=RIGID_TAP line=4 x=0 y=0 z=-1.2 scale=1" "$G33_1_STDOUT"
grep -Fq "canon_event=STOP_SPEED_FEED_SYNCH" "$G33_1_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$G33_1_STDOUT"

G6164_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.g6164.run.stdout.log"
grep -Fq "file_open=0" "$G6164_STDOUT"
grep -Fq "file_read_count=6" "$G6164_STDOUT"
grep -Fq "file_execute_count=6" "$G6164_STDOUT"
grep -Fq "canon_event=SET_MOTION_CONTROL_MODE mode=3 tolerance=0" "$G6164_STDOUT"
grep -Fq "canon_event=SET_NAIVECAM_TOLERANCE tolerance=0" "$G6164_STDOUT"
grep -Fq "canon_event=SET_MOTION_CONTROL_MODE mode=3 tolerance=1" "$G6164_STDOUT"
grep -Fq "canon_event=SET_NAIVECAM_TOLERANCE tolerance=1" "$G6164_STDOUT"
grep -Fq "canon_event=SET_NAIVECAM_TOLERANCE tolerance=2" "$G6164_STDOUT"
grep -Fq "canon_event=SET_MOTION_CONTROL_MODE mode=2 tolerance=0" "$G6164_STDOUT"
grep -Fq "canon_event=SET_MOTION_CONTROL_MODE mode=1 tolerance=0" "$G6164_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$G6164_STDOUT"

ROTATION_ABS_PTS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.rotation_abs_pts.run.stdout.log"
grep -Fq "file_open=0" "$ROTATION_ABS_PTS_STDOUT"
grep -Fq "file_read_count=192" "$ROTATION_ABS_PTS_STDOUT"
grep -Fq "file_execute_count=192" "$ROTATION_ABS_PTS_STDOUT"
grep -Fq "file_saw_error=0" "$ROTATION_ABS_PTS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$ROTATION_ABS_PTS_STDOUT"
if [[ "$(grep -Fc "canon_event=MESSAGE: 0.000000 0.000000 0.000000" "$ROTATION_ABS_PTS_STDOUT")" != "14" ]]; then
  echo "unexpected rotation/abs-pts zero absolute-position message count" >&2
  exit 1
fi
if [[ "$(grep -Fc "canon_event=MESSAGE: 1.000000 2.000000 3.000000" "$ROTATION_ABS_PTS_STDOUT")" != "14" ]]; then
  echo "unexpected rotation/abs-pts target absolute-position message count" >&2
  exit 1
fi

ROTATION_G28_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.rotation_g28.run.stdout.log"
grep -Fq "file_open=0" "$ROTATION_G28_STDOUT"
grep -Fq "file_read_count=13" "$ROTATION_G28_STDOUT"
grep -Fq "file_execute_count=13" "$ROTATION_G28_STDOUT"
grep -Fq "file_saw_error=0" "$ROTATION_G28_STDOUT"
grep -Fq "canon_event=SET_XY_ROTATION rotation=45" "$ROTATION_G28_STDOUT"
grep -Fq "canon_event=COMMENT: G55 G28" "$ROTATION_G28_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=9 x=10 y=10 z=0" "$ROTATION_G28_STDOUT"
grep -Fq "canon_event=COMMENT: G56 G28" "$ROTATION_G28_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=14 x=0 y=14.1421 z=0 a=1" "$ROTATION_G28_STDOUT"
grep -Eq "canon_event=STRAIGHT_TRAVERSE line=14 x=(0|[0-9.e-]+) y=14\\.1421 z=0 a=0" "$ROTATION_G28_STDOUT"
grep -Fq "canon_event=FINISH" "$ROTATION_G28_STDOUT"

ROTATION_G53_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.rotation_g53.run.stdout.log"
grep -Fq "file_open=0" "$ROTATION_G53_STDOUT"
grep -Fq "file_read_count=17" "$ROTATION_G53_STDOUT"
grep -Fq "file_execute_count=17" "$ROTATION_G53_STDOUT"
grep -Fq "file_saw_error=0" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=SET_XY_ROTATION rotation=45" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=COMMENT: g53 + g55 to 1,1" "$ROTATION_G53_STDOUT"
grep -Eq "canon_event=STRAIGHT_TRAVERSE line=8 x=1\\.41421 y=([0-9.e-]+) z=0" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=COMMENT: g53 + g55 + g92 to 1,1" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=13 x=0 y=0 z=0" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=COMMENT: g53 + g56 + g92 to 1,1" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=18 x=-0.414214 y=1 z=0 a=1" "$ROTATION_G53_STDOUT"
grep -Fq "canon_event=FINISH" "$ROTATION_G53_STDOUT"

SUB_CALL_FROM_SUB_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.sub_call_from_sub.run.stdout.log"
grep -Fq "file_open=0" "$SUB_CALL_FROM_SUB_STDOUT"
grep -Fq "file_read_count=10" "$SUB_CALL_FROM_SUB_STDOUT"
grep -Fq "file_execute_count=10" "$SUB_CALL_FROM_SUB_STDOUT"
grep -Fq "X caller: 1.000000 2.000000 3.000000" "$SUB_CALL_FROM_SUB_STDOUT"
grep -Fq "X helper: 4.000000 5.000000 6.000000" "$SUB_CALL_FROM_SUB_STDOUT"
grep -Fq "canon_event=COMMENT: Test: calling a sub from within another sub is valid" "$SUB_CALL_FROM_SUB_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$SUB_CALL_FROM_SUB_STDOUT"

SEQUENCE_NUMBER_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.sequence_number.run.stdout.log"
grep -Fq "file_open=0" "$SEQUENCE_NUMBER_STDOUT"
grep -Fq "file_read_count=13" "$SEQUENCE_NUMBER_STDOUT"
grep -Fq "file_execute_count=13" "$SEQUENCE_NUMBER_STDOUT"
grep -Fq "canon_event=MESSAGE: main: line=7.000000 - expect 7" "$SEQUENCE_NUMBER_STDOUT"
grep -Fq "canon_event=MESSAGE:  in rm400.ngc line=2.000000 - expect 2" "$SEQUENCE_NUMBER_STDOUT"
grep -Fq "canon_event=MESSAGE: main: line=9.000000 - expect 9" "$SEQUENCE_NUMBER_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$SEQUENCE_NUMBER_STDOUT"

NESTED_SUB_ERROR_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.nested_sub_error.run.stdout.log"
grep -Fq "file_open=0" "$NESTED_SUB_ERROR_STDOUT"
grep -Fq "file_read_4=5" "$NESTED_SUB_ERROR_STDOUT"
grep -Fq "file_read_count=4" "$NESTED_SUB_ERROR_STDOUT"
grep -Fq "file_execute_count=3" "$NESTED_SUB_ERROR_STDOUT"
grep -Fq "canon_event=COMMENT: Test: nested sub definition inside named sub should error" "$NESTED_SUB_ERROR_STDOUT"
if grep -Fq "canon_event=PROGRAM_END" "$NESTED_SUB_ERROR_STDOUT"; then
  echo "unexpected nested-sub-error program end" >&2
  exit 1
fi

NESTED_SUB_IN_FILE_ERROR_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.nested_sub_in_file_error.run.stdout.log"
grep -Fq "file_open=0" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
grep -Fq "file_execute_5=5" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
grep -Fq "file_error_text=Subroutine 'O200' not found -- not in offset table and no file '" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
grep -Fq "file_read_count=5" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
grep -Fq "file_execute_count=5" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
grep -Fq "canon_event=COMMENT: Test: numbered sub after named endsub in same file should error" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
grep -Fq "canon_event=MESSAGE:  sequential main: 7.000000 8.000000 9.000000" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"
if grep -Fq "canon_event=PROGRAM_END" "$NESTED_SUB_IN_FILE_ERROR_STDOUT"; then
  echo "unexpected nested-sub-in-file-error program end" >&2
  exit 1
fi

OWORD_UNWIND_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.oword_unwind.run.stdout.log"
grep -Fq "file_open=0" "$OWORD_UNWIND_STDOUT"
grep -Fq "file_read_5=5" "$OWORD_UNWIND_STDOUT"
grep -Fq "file_read_10=5" "$OWORD_UNWIND_STDOUT"
grep -Fq "file_read_count=11" "$OWORD_UNWIND_STDOUT"
grep -Fq "file_execute_count=9" "$OWORD_UNWIND_STDOUT"
grep -Fq "canon_event=MESSAGE:  pre divide-by-zero" "$OWORD_UNWIND_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$OWORD_UNWIND_STDOUT"
if grep -Fq "canon_event=MESSAGE:  post divide-by-zero" "$OWORD_UNWIND_STDOUT"; then
  echo "unexpected oword-unwind post-error message" >&2
  exit 1
fi

ABORT_HOT_COMMENT_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.abort_hot_comment.run.stdout.log"
grep -Fq "file_open=0" "$ABORT_HOT_COMMENT_STDOUT"
grep -Fq "file_read_count=125" "$ABORT_HOT_COMMENT_STDOUT"
grep -Fq "file_execute_count=125" "$ABORT_HOT_COMMENT_STDOUT"
grep -Fq "file_saw_error=1" "$ABORT_HOT_COMMENT_STDOUT"
grep -Fq "file_error_text= MixedCase param42=20.000000 named=4711.000000 INI=3.140000" "$ABORT_HOT_COMMENT_STDOUT"
if grep -Fq "canon_event=PROGRAM_END" "$ABORT_HOT_COMMENT_STDOUT"; then
  echo "abort-hot-comment unexpectedly reached program end" >&2
  exit 1
fi

M19_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m19.run.stdout.log"
grep -Fq "file_open=0" "$M19_STDOUT"
grep -Fq "file_read_count=8" "$M19_STDOUT"
grep -Fq "file_execute_count=8" "$M19_STDOUT"
grep -Fq "file_saw_error=0" "$M19_STDOUT"
grep -Fq "canon_event=ORIENT_SPINDLE spindle=0 orientation=87 mode=0" "$M19_STDOUT"
grep -Fq "canon_event=WAIT_SPINDLE_ORIENT_COMPLETE spindle=0 timeout=2" "$M19_STDOUT"
grep -Fq "canon_event=ORIENT_SPINDLE spindle=0 orientation=42 mode=1" "$M19_STDOUT"
grep -Fq "canon_event=ORIENT_SPINDLE spindle=0 orientation=132 mode=0" "$M19_STDOUT"
grep -Fq "canon_event=ORIENT_SPINDLE spindle=0 orientation=132 mode=1" "$M19_STDOUT"
grep -Fq "canon_event=WAIT_SPINDLE_ORIENT_COMPLETE spindle=0 timeout=1" "$M19_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M19_STDOUT"

MAGIC_COMMENTS_PARAM_FORMAT_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.magic_comments_param_format.run.stdout.log"
grep -Fq "file_open=0" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "file_read_count=13" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "file_execute_count=13" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "file_saw_error=0" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE:  native value = 1.123457" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE:  Test round to integer: 1" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE:  Test round to 4 decimals: 1.1235" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE:  Test format separate from param:  The value is: 1.1235" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE:  Test round to 7 decimals: 1.1234568" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE:  native value2 = 2.345679" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE:  Test2 round all params in line to 4 decimals:  The values are: 1.1235,  2.3457" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=MESSAGE:  Test2 only round last value to integer: The values are: 1.123457,  2" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$MAGIC_COMMENTS_PARAM_FORMAT_STDOUT"

M98M99_01_BASICS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_01_basics.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_01_BASICS_STDOUT"
grep -Fq "file_read_count=22" "$M98M99_01_BASICS_STDOUT"
grep -Fq "file_execute_count=22" "$M98M99_01_BASICS_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_01_BASICS_STDOUT"
grep -Fq "canon_event=COMMENT: A simple O sub example " "$M98M99_01_BASICS_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=10 x=3 y=0 z=0.25" "$M98M99_01_BASICS_STDOUT"
grep -Fq "canon_event=STRAIGHT_FEED line=11 x=3 y=0 z=-1" "$M98M99_01_BASICS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_01_BASICS_STDOUT"

M98M99_02_VARIABLES_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_02_variables.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_02_VARIABLES_STDOUT"
grep -Fq "file_read_count=54" "$M98M99_02_VARIABLES_STDOUT"
grep -Fq "file_execute_count=54" "$M98M99_02_VARIABLES_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_02_VARIABLES_STDOUT"
grep -Fq "Q MAIN/FANUC:  POST-M98:  1=13.010000; 30=13.300000; 31=13.310000" "$M98M99_02_VARIABLES_STDOUT"
grep -Fq "Q MAIN/RS274NGC:  POST-O-CALL:  1=42.010000; 30=42.300000; 31=13.310000" "$M98M99_02_VARIABLES_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_02_VARIABLES_STDOUT"

M98M99_03_ERROR_M98_NO_P_WORD_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_03_error_m98_no_p_word.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_03_ERROR_M98_NO_P_WORD_STDOUT"
grep -Fq "file_read_count=3" "$M98M99_03_ERROR_M98_NO_P_WORD_STDOUT"
grep -Fq "file_execute_count=2" "$M98M99_03_ERROR_M98_NO_P_WORD_STDOUT"
grep -Fq "file_saw_error=1" "$M98M99_03_ERROR_M98_NO_P_WORD_STDOUT"
grep -Fq "file_error_text=Found 'm98' code with no P-word" \
  "$M98M99_03_ERROR_M98_NO_P_WORD_STDOUT"
grep -Fq "run_step phase=read step=3 rc=5 line=4" "$M98M99_03_ERROR_M98_NO_P_WORD_STDOUT"
grep -Fq "statement_uri=M98" "$M98M99_03_ERROR_M98_NO_P_WORD_STDOUT"
grep -Fq "canon_event=COMMENT: Fanuc M98 requires P-word " \
  "$M98M99_03_ERROR_M98_NO_P_WORD_STDOUT"

M98M99_04_M98_BUT_NO_SUB_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_04_m98_but_no_sub.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_04_M98_BUT_NO_SUB_STDOUT"
grep -Fq "file_read_count=5" "$M98M99_04_M98_BUT_NO_SUB_STDOUT"
grep -Fq "file_execute_count=4" "$M98M99_04_M98_BUT_NO_SUB_STDOUT"
grep -Fq "file_saw_error=1" "$M98M99_04_M98_BUT_NO_SUB_STDOUT"
grep -Fq "file_error_text=Failed to find sub 'O1' before EOF" \
  "$M98M99_04_M98_BUT_NO_SUB_STDOUT"
grep -Fq "run_step phase=read step=5 rc=5 line=4" "$M98M99_04_M98_BUT_NO_SUB_STDOUT"
grep -Fq "statement_uri=%25" "$M98M99_04_M98_BUT_NO_SUB_STDOUT"
grep -Fq "canon_event=COMMENT: M98 call non-existant sub " \
  "$M98M99_04_M98_BUT_NO_SUB_STDOUT"

M98M99_05_M98_LOOPS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_05_m98_loops.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_05_M98_LOOPS_STDOUT"
grep -Fq "file_read_count=102" "$M98M99_05_M98_LOOPS_STDOUT"
grep -Fq "file_execute_count=102" "$M98M99_05_M98_LOOPS_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_05_M98_LOOPS_STDOUT"
grep -Fq "X MAIN 10LOOP PARAM1 = 10.000000" "$M98M99_05_M98_LOOPS_STDOUT"
grep -Fq "X MAIN 0LOOP PARAM1 = 0.000000" "$M98M99_05_M98_LOOPS_STDOUT"
grep -Fq "X MAIN 2LOOP PARAM1 = 2.000000" "$M98M99_05_M98_LOOPS_STDOUT"
grep -Fq "X SUB O1 PARAM1 = 10.000000" "$M98M99_05_M98_LOOPS_STDOUT"
grep -Fq "canon_event=COMMENT: A simple O sub example " "$M98M99_05_M98_LOOPS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_05_M98_LOOPS_STDOUT"
if ! awk '
  /statement_uri=M98%20P1%20L0/ { in_l0 = 1; saw_sub = 0; next }
  in_l0 && /X SUB O1 PARAM1/ { saw_sub = 1 }
  in_l0 && /X MAIN 0LOOP PARAM1 = 0\.000000/ && !saw_sub { found = 1; in_l0 = 0 }
  END { exit(found ? 0 : 1) }
' "$M98M99_05_M98_LOOPS_STDOUT"; then
  echo "unexpected m98m99 M98 L0 loop execution" >&2
  exit 1
fi

M98M99_06_CALLED_WITH_O_CALL_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_06_called_with_o_call.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_06_CALLED_WITH_O_CALL_STDOUT"
grep -Fq "file_read_count=5" "$M98M99_06_CALLED_WITH_O_CALL_STDOUT"
grep -Fq "file_execute_count=5" "$M98M99_06_CALLED_WITH_O_CALL_STDOUT"
grep -Fq "file_saw_error=1" "$M98M99_06_CALLED_WITH_O_CALL_STDOUT"
grep -Fq "file_error_text=Fanuc 'O....' subroutine must be called with 'M98'" \
  "$M98M99_06_CALLED_WITH_O_CALL_STDOUT"
grep -Fq "run_step phase=execute step=5 rc=5 line=4" "$M98M99_06_CALLED_WITH_O_CALL_STDOUT"
grep -Fq "statement_uri=O1" "$M98M99_06_CALLED_WITH_O_CALL_STDOUT"
grep -Fq "canon_event=COMMENT: Fanuc 'O...' sub must be called with M98 " \
  "$M98M99_06_CALLED_WITH_O_CALL_STDOUT"

M98M99_06_ENDED_WITH_O_ENDSUB_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_06_ended_with_o_endsub.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_06_ENDED_WITH_O_ENDSUB_STDOUT"
grep -Fq "file_read_count=7" "$M98M99_06_ENDED_WITH_O_ENDSUB_STDOUT"
grep -Fq "file_execute_count=7" "$M98M99_06_ENDED_WITH_O_ENDSUB_STDOUT"
grep -Fq "file_saw_error=1" "$M98M99_06_ENDED_WITH_O_ENDSUB_STDOUT"
grep -Fq "file_error_text=Fanuc 'O....' subroutine definition must end with 'M99'" \
  "$M98M99_06_ENDED_WITH_O_ENDSUB_STDOUT"
grep -Fq "run_step phase=execute step=7 rc=5 line=4" "$M98M99_06_ENDED_WITH_O_ENDSUB_STDOUT"
grep -Fq "statement_uri=O1%20endsub" "$M98M99_06_ENDED_WITH_O_ENDSUB_STDOUT"
grep -Fq "X FANUC" "$M98M99_06_ENDED_WITH_O_ENDSUB_STDOUT"
grep -Fq "canon_event=COMMENT: Fanuc 'O...' sub must end with M99 " \
  "$M98M99_06_ENDED_WITH_O_ENDSUB_STDOUT"

M98M99_06_SUB_CALLED_WITH_M98_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_06_sub_called_with_m98.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_06_SUB_CALLED_WITH_M98_STDOUT"
grep -Fq "file_read_count=5" "$M98M99_06_SUB_CALLED_WITH_M98_STDOUT"
grep -Fq "file_execute_count=5" "$M98M99_06_SUB_CALLED_WITH_M98_STDOUT"
grep -Fq "file_saw_error=1" "$M98M99_06_SUB_CALLED_WITH_M98_STDOUT"
grep -Fq "file_error_text='O.... sub' subroutine must be called with 'O.... call'" \
  "$M98M99_06_SUB_CALLED_WITH_M98_STDOUT"
grep -Fq "run_step phase=execute step=5 rc=5 line=4" "$M98M99_06_SUB_CALLED_WITH_M98_STDOUT"
grep -Fq "statement_uri=O1%20sub" "$M98M99_06_SUB_CALLED_WITH_M98_STDOUT"
grep -Fq "canon_event=COMMENT: RS274NGC 'O...' sub must not be called with M98 " \
  "$M98M99_06_SUB_CALLED_WITH_M98_STDOUT"

M98M99_06_SUB_ENDED_WITH_M99_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_06_sub_ended_with_m99.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_06_SUB_ENDED_WITH_M99_STDOUT"
grep -Fq "file_read_count=7" "$M98M99_06_SUB_ENDED_WITH_M99_STDOUT"
grep -Fq "file_execute_count=7" "$M98M99_06_SUB_ENDED_WITH_M99_STDOUT"
grep -Fq "file_saw_error=1" "$M98M99_06_SUB_ENDED_WITH_M99_STDOUT"
grep -Fq "file_error_text='O.... endsub' or 'O.... return' must follow 'O.... sub' subroutine definition" \
  "$M98M99_06_SUB_ENDED_WITH_M99_STDOUT"
grep -Fq "run_step phase=execute step=7 rc=5 line=4" "$M98M99_06_SUB_ENDED_WITH_M99_STDOUT"
grep -Fq "statement_uri=M99" "$M98M99_06_SUB_ENDED_WITH_M99_STDOUT"
grep -Fq "X FANUC" "$M98M99_06_SUB_ENDED_WITH_M99_STDOUT"
grep -Fq "canon_event=COMMENT: RS274NGC 'O... sub' sub must not end with M99 " \
  "$M98M99_06_SUB_ENDED_WITH_M99_STDOUT"

M98M99_07_NESTED_SUBS_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_07_nested_subs.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_07_NESTED_SUBS_STDOUT"
grep -Fq "file_read_count=163" "$M98M99_07_NESTED_SUBS_STDOUT"
grep -Fq "file_execute_count=163" "$M98M99_07_NESTED_SUBS_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_07_NESTED_SUBS_STDOUT"
grep -Fq "X >>>> LOOP [O2.O1]:  4.500000" "$M98M99_07_NESTED_SUBS_STDOUT"
grep -Fq "X MAIN END:  1=5.000000" "$M98M99_07_NESTED_SUBS_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_07_NESTED_SUBS_STDOUT"

M98M99_08_SUB_FOLLOWS_MAIN_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_08_sub_follows_main.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
grep -Fq "file_read_count=9" "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
grep -Fq "file_execute_count=9" "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
grep -Fq "X IN O1" "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
grep -Fq "canon_event=COMMENT: Fanuc-style subs may follow main program " "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_08_SUB_FOLLOWS_MAIN_STDOUT"

check_m98m99_09_fanuc_pass() {
  local stdout_file="$1"
  grep -Fq "file_open=0" "$stdout_file"
  grep -Fq "file_read_count=22" "$stdout_file"
  grep -Fq "file_execute_count=22" "$stdout_file"
  grep -Fq "file_saw_error=0" "$stdout_file"
  grep -Fq "canon_event=COMMENT: A simple O sub example " "$stdout_file"
  grep -Fq "canon_event=STRAIGHT_TRAVERSE line=10 x=3 y=0 z=0.25" "$stdout_file"
  grep -Fq "canon_event=STRAIGHT_FEED line=11 x=3 y=0 z=-1" "$stdout_file"
  grep -Fq "canon_event=PROGRAM_END" "$stdout_file"
  if grep -Fq "file_error_text=DISABLE_FANUC_STYLE_SUB" "$stdout_file"; then
    echo "unexpected disabled Fanuc sub error in $stdout_file" >&2
    exit 1
  fi
}

check_m98m99_09_rs274ngc_pass() {
  local stdout_file="$1"
  grep -Fq "file_open=0" "$stdout_file"
  grep -Fq "file_read_count=15" "$stdout_file"
  grep -Fq "file_execute_count=15" "$stdout_file"
  grep -Fq "file_saw_error=0" "$stdout_file"
  grep -Fq "canon_event=COMMENT: A simple O sub example " "$stdout_file"
  grep -Fq "canon_event=STRAIGHT_TRAVERSE line=5 x=1 y=0 z=0.25" "$stdout_file"
  grep -Fq "canon_event=STRAIGHT_FEED line=6 x=1 y=0 z=-1" "$stdout_file"
  grep -Fq "canon_event=PROGRAM_END" "$stdout_file"
  if grep -Fq "file_error_text=DISABLE_FANUC_STYLE_SUB" "$stdout_file"; then
    echo "unexpected disabled Fanuc sub error in $stdout_file" >&2
    exit 1
  fi
}

check_m98m99_09_fanuc_pass \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_09_fanuc_ini.run.stdout.log"
check_m98m99_09_rs274ngc_pass \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_09_rs274ngc_ini.run.stdout.log"
check_m98m99_09_rs274ngc_pass \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_09_rs274ngc_no_fanuc.run.stdout.log"
check_m98m99_09_fanuc_pass \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_09_fanuc_default.run.stdout.log"
check_m98m99_09_rs274ngc_pass \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_09_rs274ngc_default.run.stdout.log"

M98M99_09_FANUC_NO_FANUC_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_09_fanuc_no_fanuc.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_09_FANUC_NO_FANUC_STDOUT"
grep -Fq "file_read_count=5" "$M98M99_09_FANUC_NO_FANUC_STDOUT"
grep -Fq "file_execute_count=4" "$M98M99_09_FANUC_NO_FANUC_STDOUT"
grep -Fq "file_saw_error=1" "$M98M99_09_FANUC_NO_FANUC_STDOUT"
grep -Fq "file_error_text=DISABLE_FANUC_STYLE_SUB set in INI file, but found m98" \
  "$M98M99_09_FANUC_NO_FANUC_STDOUT"
grep -Fq "run_step phase=read step=5 rc=5 line=6" "$M98M99_09_FANUC_NO_FANUC_STDOUT"
grep -Fq "statement_uri=M98%20P1%20L3" "$M98M99_09_FANUC_NO_FANUC_STDOUT"
grep -Fq "canon_event=STRAIGHT_TRAVERSE line=5 x=0 y=0 z=0.25" \
  "$M98M99_09_FANUC_NO_FANUC_STDOUT"
if grep -Fq "canon_event=PROGRAM_END" "$M98M99_09_FANUC_NO_FANUC_STDOUT"; then
  echo "unexpected m98m99 disabled Fanuc sub program end" >&2
  exit 1
fi

M98M99_10_M98_P001_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_10_m98_p001.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_10_M98_P001_STDOUT"
grep -Fq "file_read_count=8" "$M98M99_10_M98_P001_STDOUT"
grep -Fq "file_execute_count=8" "$M98M99_10_M98_P001_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_10_M98_P001_STDOUT"
grep -Fq "x got here" "$M98M99_10_M98_P001_STDOUT"
grep -Fq "canon_event=COMMENT: main program" "$M98M99_10_M98_P001_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_10_M98_P001_STDOUT"
if awk '
  /^file_read_1=0$/ { in_file = 1 }
  in_file && /ERROR: should not get here/ { found = 1 }
  END { exit(found ? 0 : 1) }
' "$M98M99_10_M98_P001_STDOUT"; then
  echo "unexpected m98m99 leading-zero O-word fallback branch" >&2
  exit 1
fi

check_m98m99_11_legal() {
  local stdout_file="$1"
  local read_count="$2"
  local first_line="$3"
  local sub_line="$4"

  grep -Fq "file_open=0" "$stdout_file"
  grep -Fq "file_read_count=$read_count" "$stdout_file"
  grep -Fq "file_execute_count=$read_count" "$stdout_file"
  grep -Fq "file_saw_error=0" "$stdout_file"
  grep -Fq "canon_event=COMMENT: legal example" "$stdout_file"
  grep -Fq "canon_event=STRAIGHT_TRAVERSE line=$first_line x=1 y=2 z=0" "$stdout_file"
  grep -Fq "canon_event=STRAIGHT_TRAVERSE line=$sub_line x=1 y=2 z=3" "$stdout_file"
  if grep -Fq "should never get here" "$stdout_file"; then
    echo "unexpected m98m99 main-program fallthrough in $stdout_file" >&2
    exit 1
  fi
}

check_m98m99_11_legal \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_11_legal_m2.run.stdout.log" \
  17 4 13
grep -Fq "canon_event=PROGRAM_END" \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_11_legal_m2.run.stdout.log"
check_m98m99_11_legal \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_11_legal_m02.run.stdout.log" \
  17 4 13
grep -Fq "canon_event=PROGRAM_END" \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_11_legal_m02.run.stdout.log"
check_m98m99_11_legal \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_11_legal_m30.run.stdout.log" \
  17 4 13
grep -Fq "canon_event=PROGRAM_END" \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_11_legal_m30.run.stdout.log"
check_m98m99_11_legal \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_11_legal_percent.run.stdout.log" \
  20 11 6
grep -Fq "canon_event=FINISH" \
  "$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_11_legal_percent.run.stdout.log"

M98M99_11_ILLEGAL_EOF_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_11_illegal_eof.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_11_ILLEGAL_EOF_STDOUT"
grep -Fq "file_read_count=21" "$M98M99_11_ILLEGAL_EOF_STDOUT"
grep -Fq "file_execute_count=20" "$M98M99_11_ILLEGAL_EOF_STDOUT"
grep -Fq "file_saw_error=1" "$M98M99_11_ILLEGAL_EOF_STDOUT"
grep -Fq "file_error_text=File ended with no percent sign (%) or program end (M2)" \
  "$M98M99_11_ILLEGAL_EOF_STDOUT"
grep -Fq "canon_event=COMMENT: end of main signaled by EOF" \
  "$M98M99_11_ILLEGAL_EOF_STDOUT"

M98M99_11_ILLEGAL_NO_M30_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_11_illegal_no_m30.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_11_ILLEGAL_NO_M30_STDOUT"
grep -Fq "file_read_count=18" "$M98M99_11_ILLEGAL_NO_M30_STDOUT"
grep -Fq "file_execute_count=18" "$M98M99_11_ILLEGAL_NO_M30_STDOUT"
grep -Fq "file_saw_error=1" "$M98M99_11_ILLEGAL_NO_M30_STDOUT"
grep -Fq "sub: o|2| found in illegal location" "$M98M99_11_ILLEGAL_NO_M30_STDOUT"
grep -Fq "statement_uri=O2%20sub%20%28subprogram%20begin%29" \
  "$M98M99_11_ILLEGAL_NO_M30_STDOUT"

M98M99_11_ILLEGAL_SUB_AFTER_PERCENT_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_11_illegal_sub_after_percent.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_11_ILLEGAL_SUB_AFTER_PERCENT_STDOUT"
grep -Fq "file_read_count=8" "$M98M99_11_ILLEGAL_SUB_AFTER_PERCENT_STDOUT"
grep -Fq "file_execute_count=7" "$M98M99_11_ILLEGAL_SUB_AFTER_PERCENT_STDOUT"
grep -Fq "file_saw_error=1" "$M98M99_11_ILLEGAL_SUB_AFTER_PERCENT_STDOUT"
grep -Fq "file_error_text=Failed to find sub 'O2' before EOF" \
  "$M98M99_11_ILLEGAL_SUB_AFTER_PERCENT_STDOUT"
grep -Fq "canon_event=FINISH" "$M98M99_11_ILLEGAL_SUB_AFTER_PERCENT_STDOUT"

M98M99_13_NAMED_PROGRAM_NAMED_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_13_named_program_named.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
grep -Fq "file_read_count=4" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
grep -Fq "file_execute_count=4" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
grep -Fq "canon_event=COMMENT: test-named.ngc:  Test named programs" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
grep -Fq "canon_event=COMMENT: ...program body" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_13_NAMED_PROGRAM_NAMED_STDOUT"

M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_13_named_program_numbered.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
grep -Fq "file_read_count=4" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
grep -Fq "file_execute_count=4" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
grep -Fq "canon_event=COMMENT: test-numbered.ngc:  Test numbered programs" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
grep -Fq "canon_event=COMMENT: ...program body" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_13_NAMED_PROGRAM_NUMBERED_STDOUT"

M98M99_14_O_EXPRESSION_CALL_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.m98m99_14_o_expression_call.run.stdout.log"
grep -Fq "file_open=0" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
grep -Fq "file_read_count=29" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
grep -Fq "file_execute_count=29" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
grep -Fq "file_saw_error=0" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
grep -Fq "In sub 100" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
grep -Fq "In sub 200" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$M98M99_14_O_EXPRESSION_CALL_STDOUT"

G10_L1_L10_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.g10_l1_l10.run.stdout.log"
grep -Fq "file_open=0" "$G10_L1_L10_STDOUT"
grep -Fq "file_read_count=115" "$G10_L1_L10_STDOUT"
grep -Fq "file_execute_count=115" "$G10_L1_L10_STDOUT"
grep -Fq "file_saw_error=0" "$G10_L1_L10_STDOUT"
grep -Fq "canon_event=MESSAGE: G10 L1:  set tool offsets direct" "$G10_L1_L10_STDOUT"
grep -Fq "canon_event=MESSAGE: G10 L10:  set tool offsets relative to position + 45 deg. rotation" "$G10_L1_L10_STDOUT"
grep -Fq "canon_event=MESSAGE: Model B contract: M6 alone and G10 alone do not change tool offset params" "$G10_L1_L10_STDOUT"
grep -Fq "canon_event=MESSAGE:  G10 alone does not apply offset, should still be 0 0 0: 0.000000 0.000000 0.000000" "$G10_L1_L10_STDOUT"
grep -Fq "canon_event=USE_TOOL_LENGTH_OFFSET x=8 y=6 z=7" "$G10_L1_L10_STDOUT"
grep -Fq "canon_event=SET_XY_ROTATION rotation=45" "$G10_L1_L10_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$G10_L1_L10_STDOUT"

G10_L11_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.g10_l11.run.stdout.log"
grep -Fq "file_open=0" "$G10_L11_STDOUT"
grep -Fq "file_read_count=41" "$G10_L11_STDOUT"
grep -Fq "file_execute_count=41" "$G10_L11_STDOUT"
grep -Fq "file_saw_error=0" "$G10_L11_STDOUT"
grep -Fq "canon_event=SET_G5X_OFFSET index=1 x=1 y=2 z=-3" "$G10_L11_STDOUT"
grep -Fq "canon_event=SET_G92_OFFSET x=-41.1962 y=-46.1962 z=-72" "$G10_L11_STDOUT"
grep -Fq "canon_event=USE_TOOL_LENGTH_OFFSET x=0 y=0 z=-4" "$G10_L11_STDOUT"
grep -Fq "canon_event=MESSAGE: -101.600000" "$G10_L11_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$G10_L11_STDOUT"

G10_L2_WHILE_ACTIVE_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.g10_l2_while_active.run.stdout.log"
grep -Fq "file_open=0" "$G10_L2_WHILE_ACTIVE_STDOUT"
grep -Fq "file_read_count=24" "$G10_L2_WHILE_ACTIVE_STDOUT"
grep -Fq "file_execute_count=24" "$G10_L2_WHILE_ACTIVE_STDOUT"
grep -Fq "file_saw_error=0" "$G10_L2_WHILE_ACTIVE_STDOUT"
grep -Fq "canon_event=SET_G5X_OFFSET index=1 x=1 y=0 z=0" "$G10_L2_WHILE_ACTIVE_STDOUT"
grep -Fq "canon_event=SET_XY_ROTATION rotation=-45" "$G10_L2_WHILE_ACTIVE_STDOUT"
grep -Fq "canon_event=MESSAGE:  Should be 0 1.414214: 0.000000 1.414214" "$G10_L2_WHILE_ACTIVE_STDOUT"
grep -Fq "canon_event=SET_XY_ROTATION rotation=90" "$G10_L2_WHILE_ACTIVE_STDOUT"
grep -Fq "canon_event=MESSAGE:  Should be 1 1: 1.000000 1.000000" "$G10_L2_WHILE_ACTIVE_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$G10_L2_WHILE_ACTIVE_STDOUT"

G10_L20_WHILE_ACTIVE_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.g10_l20_while_active.run.stdout.log"
grep -Fq "file_open=0" "$G10_L20_WHILE_ACTIVE_STDOUT"
grep -Fq "file_read_count=20" "$G10_L20_WHILE_ACTIVE_STDOUT"
grep -Fq "file_execute_count=20" "$G10_L20_WHILE_ACTIVE_STDOUT"
grep -Fq "file_saw_error=0" "$G10_L20_WHILE_ACTIVE_STDOUT"
grep -Fq "canon_event=SET_XY_ROTATION rotation=45" "$G10_L20_WHILE_ACTIVE_STDOUT"
grep -Fq "canon_event=MESSAGE:  Should be -1 0: -1.000000 0.000000" "$G10_L20_WHILE_ACTIVE_STDOUT"
grep -Fq "canon_event=MESSAGE:  Should be 0 -1: 0.000000 -1.000000" "$G10_L20_WHILE_ACTIVE_STDOUT"
grep -Fq "canon_event=MESSAGE:  Should be 1 1: 1.000000 1.000000" "$G10_L20_WHILE_ACTIVE_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$G10_L20_WHILE_ACTIVE_STDOUT"

G10_WITH_G92_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.g10_with_g92.run.stdout.log"
grep -Fq "file_open=0" "$G10_WITH_G92_STDOUT"
grep -Fq "file_read_count=61" "$G10_WITH_G92_STDOUT"
grep -Fq "file_execute_count=61" "$G10_WITH_G92_STDOUT"
grep -Fq "file_saw_error=0" "$G10_WITH_G92_STDOUT"
grep -Fq "canon_event=SET_G5X_OFFSET index=9 x=25 y=26 z=27" "$G10_WITH_G92_STDOUT"
grep -Fq "canon_event=SET_G92_OFFSET x=-0.1 y=-0.2 z=-10.3" "$G10_WITH_G92_STDOUT"
grep -Fq "canon_event=MESSAGE:  X0.100000 Y0.200000 Z0.300000" "$G10_WITH_G92_STDOUT"
grep -Fq "canon_event=MESSAGE:  X-23.900000 Y-23.800000 Z-23.700000" "$G10_WITH_G92_STDOUT"
grep -Fq "canon_event=SET_G92_OFFSET x=0 y=0 z=0" "$G10_WITH_G92_STDOUT"
grep -Fq "canon_event=MESSAGE:  X-24.000000 Y-24.000000 Z-34.000000" "$G10_WITH_G92_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$G10_WITH_G92_STDOUT"

G52_G92_INTERACTION_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.g52_g92_interaction.run.stdout.log"
grep -Fq "file_open=0" "$G52_G92_INTERACTION_STDOUT"
grep -Fq "file_read_count=39" "$G52_G92_INTERACTION_STDOUT"
grep -Fq "file_execute_count=39" "$G52_G92_INTERACTION_STDOUT"
grep -Fq "file_saw_error=0" "$G52_G92_INTERACTION_STDOUT"
grep -Fq "canon_event=COMMENT: G52 and G92 param #5210 setting behavior" "$G52_G92_INTERACTION_STDOUT"
grep -Fq "G92 params:  1.000000 -25.400000 -50.800000" "$G52_G92_INTERACTION_STDOUT"
grep -Fq "G92 params:  0.000000 -25.400000 -50.800000" "$G52_G92_INTERACTION_STDOUT"
grep -Fq "G92 params:  1.000000 50.800000 76.200000" "$G52_G92_INTERACTION_STDOUT"
grep -Fq "G92 params:  1.000000 0.000000 0.000000" "$G52_G92_INTERACTION_STDOUT"
grep -Fq "canon_event=SET_G92_OFFSET x=-1 y=-2 z=0" "$G52_G92_INTERACTION_STDOUT"
grep -Fq "canon_event=SET_G92_OFFSET x=2 y=3 z=0" "$G52_G92_INTERACTION_STDOUT"
grep -Fq "canon_event=PALLET_SHUTTLE" "$G52_G92_INTERACTION_STDOUT"
grep -Fq "canon_event=PROGRAM_END" "$G52_G92_INTERACTION_STDOUT"

G92_PERSISTENCE_ENABLED_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.g92_persistence_enabled.run.stdout.log"
grep -Fq "file_open=0" "$G92_PERSISTENCE_ENABLED_STDOUT"
grep -Fq "file_saw_error=0" "$G92_PERSISTENCE_ENABLED_STDOUT"
grep -Fq "Startup G92 params: 1.000000 1.250000 2.500000 3.750000" "$G92_PERSISTENCE_ENABLED_STDOUT"
grep -Fq "canon_event=SET_G92_OFFSET x=1.25 y=2.5 z=3.75" "$G92_PERSISTENCE_ENABLED_STDOUT"

G92_PERSISTENCE_DISABLED_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.g92_persistence_disabled.run.stdout.log"
grep -Fq "file_open=0" "$G92_PERSISTENCE_DISABLED_STDOUT"
grep -Fq "file_saw_error=0" "$G92_PERSISTENCE_DISABLED_STDOUT"
grep -Fq "Startup G92 params: 0.000000 0.000000 0.000000 0.000000" "$G92_PERSISTENCE_DISABLED_STDOUT"
grep -Fq "canon_event=SET_G92_OFFSET x=0 y=0 z=0" "$G92_PERSISTENCE_DISABLED_STDOUT"

for fixture in "$GCODE_FIXTURE_DIR"/*.ngc; do
  name="$(basename "$fixture" .ngc)"
  if [[ "$name" == "disable_fanuc_style_sub_m98" ]]; then
    continue
  fi

  expected="$CANON_FIXTURE_DIR/$name.events"
  if [[ ! -f "$expected" ]]; then
    echo "missing expected canon fixture: $expected" >&2
    exit 1
  fi

  stdout_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stdout.log"
  stderr_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stderr.log"
  INI_FILE_NAME="$NAMEDPARAM_INI_FIXTURE" "$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" \
    >"$stdout_file" \
    2>"$stderr_file"
  require_mdi=1
  if [[ "$name" == "oword_subroutine" || "$name" == "percent_file_finish" ]]; then
    require_mdi=0
  fi
  if [[ "$name" == "namedparam_semantics" ||
        "$name" == "namedparam_ini_semantics" ||
        "$name" == "ini_tool_table" ||
        "$name" == "spindle_orient_offset" ]]; then
    continue
  fi
  check_fixture_output "$fixture" "$expected" "$stdout_file" "$require_mdi"
done

for name in namedparam_semantics namedparam_ini_semantics; do
  fixture="$GCODE_FIXTURE_DIR/$name.ngc"
  expected="$CANON_FIXTURE_DIR/$name.events"
  stdout_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.with_ini.stdout.log"
  stderr_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.with_ini.stderr.log"
  "$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" "$NAMEDPARAM_INI_FIXTURE" \
    >"$stdout_file" \
    2>"$stderr_file"
  check_fixture_output "$fixture" "$expected" "$stdout_file"
done

INI_TOOL_TABLE_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.ini_tool_table.with_ini.stdout.log"
INI_TOOL_TABLE_STDERR="$BUILD_DIR/linuxcnc_interp_minimal_harness.ini_tool_table.with_ini.stderr.log"
"$BUILD_DIR/linuxcnc_interp_minimal_harness" \
  "$GCODE_FIXTURE_DIR/ini_tool_table.ngc" \
  "$ROOT_DIR/tests/fixtures/ini/ini_tool_table.ini" \
  >"$INI_TOOL_TABLE_STDOUT" \
  2>"$INI_TOOL_TABLE_STDERR"
check_fixture_output \
  "$GCODE_FIXTURE_DIR/ini_tool_table.ngc" \
  "$CANON_FIXTURE_DIR/ini_tool_table.events" \
  "$INI_TOOL_TABLE_STDOUT"

SPINDLE_ORIENT_OFFSET_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.spindle_orient_offset.with_ini.stdout.log"
SPINDLE_ORIENT_OFFSET_STDERR="$BUILD_DIR/linuxcnc_interp_minimal_harness.spindle_orient_offset.with_ini.stderr.log"
"$BUILD_DIR/linuxcnc_interp_minimal_harness" \
  "$GCODE_FIXTURE_DIR/spindle_orient_offset.ngc" \
  "$SPINDLE_ORIENT_OFFSET_INI_FIXTURE" \
  >"$SPINDLE_ORIENT_OFFSET_STDOUT" \
  2>"$SPINDLE_ORIENT_OFFSET_STDERR"
check_fixture_output \
  "$GCODE_FIXTURE_DIR/spindle_orient_offset.ngc" \
  "$CANON_FIXTURE_DIR/spindle_orient_offset.events" \
  "$SPINDLE_ORIENT_OFFSET_STDOUT"

DISABLE_FANUC_STYLE_SUB_STDOUT="$BUILD_DIR/linuxcnc_interp_minimal_harness.disable_fanuc_style_sub_m98.with_ini.stdout.log"
DISABLE_FANUC_STYLE_SUB_STDERR="$BUILD_DIR/linuxcnc_interp_minimal_harness.disable_fanuc_style_sub_m98.with_ini.stderr.log"
"$BUILD_DIR/linuxcnc_interp_minimal_harness" \
  "$GCODE_FIXTURE_DIR/disable_fanuc_style_sub_m98.ngc" \
  "$DISABLE_FANUC_STYLE_SUB_INI_FIXTURE" \
  >"$DISABLE_FANUC_STYLE_SUB_STDOUT" \
  2>"$DISABLE_FANUC_STYLE_SUB_STDERR"
grep -Fq "file_open=0" "$DISABLE_FANUC_STYLE_SUB_STDOUT"
grep -Fq "file_saw_error=1" "$DISABLE_FANUC_STYLE_SUB_STDOUT"
grep -Fq "file_error_text=DISABLE_FANUC_STYLE_SUB set in INI file, but found m98" \
  "$DISABLE_FANUC_STYLE_SUB_STDOUT"

for fixture in "$GCODE_ERROR_FIXTURE_DIR"/*.ngc; do
  name="$(basename "$fixture" .ngc)"
  expected="$CANON_ERROR_FIXTURE_DIR/$name.expected"
  if [[ ! -f "$expected" ]]; then
    echo "missing expected error fixture: $expected" >&2
    exit 1
  fi

  stdout_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stdout.log"
  stderr_file="$BUILD_DIR/linuxcnc_interp_minimal_harness.$name.stderr.log"
  INI_FILE_NAME="$NAMEDPARAM_INI_FIXTURE" "$BUILD_DIR/linuxcnc_interp_minimal_harness" "$fixture" \
    >"$stdout_file" \
    2>"$stderr_file"

  while IFS= read -r expected_line; do
    [[ -z "$expected_line" ]] && continue
    if [[ "$expected_line" == absent=* ]]; then
      forbidden="${expected_line#absent=}"
      if grep -Fq "$forbidden" "$stdout_file"; then
        echo "unexpected output in $stdout_file: $forbidden" >&2
        exit 1
      fi
    elif [[ "$expected_line" == error_text=* ]]; then
      grep -Fq "$expected_line" "$stdout_file"
    else
      grep -Fq "$expected_line" "$stdout_file"
    fi
  done < "$expected"
done

printf '%s\n' "$NATIVE_PROBE_VALIDATION_FINGERPRINT" > "$VALIDATION_CACHE_KEY_FILE"
printf '%s\n' "$NATIVE_PROBE_VALIDATION_FINGERPRINT" > "$VALIDATION_CACHE_OK_FILE"
echo "native probe validation complete"
