all repos — xyw @ 769da7ca35a91d6fb7982a7a8adadca3c554b571

A minimal virtual machine and assembler for terminals.

test.sh

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
#!/usr/bin/env bash
set -euo pipefail

### Test cases
TEST_CASES=(
  "run_test helloworld.xyw assert_output_equals \$'Hello, World!\\n'"
  "run_test d6.xyw assert_output_in_range 1 6"
  "run_test on-error.xyw assert_output_equals \$'Error: Division by zero.\\nError: Unknown error.\\n'"
)

### Helper functions

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
EXAMPLES_DIR="$ROOT_DIR/examples"

fail() {
  echo "ERROR: $*" >&2
  exit 1
}

dump_file_if_exists() {
  local label="$1"
  local path="$2"
  if [[ -f "$path" ]]; then
    (tr -d '\r' <"$path" | cat -v >&2 || true)
  fi
}

need_file() {
  local path="$1"
  [[ -f "$path" ]] || fail "Missing file: $path"
}

build_xyw() {
  if command -v make >/dev/null 2>&1; then
    (cd "$ROOT_DIR" && make xyw)
  else
    echo "WARN: 'make' not found; using existing xyw binary if present." >&2
  fi
}

clean_examples_images() {
  # Only clean generated images in examples/.
  # (Keeps repository-wide behavior conservative.)
  shopt -s nullglob
  local images=("$EXAMPLES_DIR"/*.xim)
  shopt -u nullglob

  if ((${#images[@]} > 0)); then
    rm -f "${images[@]}"
  fi
}

find_xyw_bin() {
  if [[ -x "$ROOT_DIR/xyw" ]]; then
    echo "$ROOT_DIR/xyw"
    return 0
  fi
  if [[ -x "$ROOT_DIR/xyw.exe" ]]; then
    echo "$ROOT_DIR/xyw.exe"
    return 0
  fi
  fail "Could not find executable xyw binary (expected $ROOT_DIR/xyw or $ROOT_DIR/xyw.exe)."
}

assemble_in_examples_dir() {
  local xyw_bin="$1"
  local src_basename="$2" # e.g. helloworld.xyw

  need_file "$EXAMPLES_DIR/$src_basename"

  local asm_log="$TMPDIR/assemble.${src_basename%.xyw}.log"
  if ! (cd "$EXAMPLES_DIR" && "$xyw_bin" "$src_basename" >"$asm_log" 2>&1); then
    dump_file_if_exists "Assembly output" "$asm_log"
    fail "Assembly failed for $src_basename"
  fi

  # Show assembler output, but keep this function's stdout reserved for the
  # produced .xim basename (so callers can capture it).
  dump_file_if_exists "Assemble $src_basename" "$asm_log"

  local img_basename="${src_basename%.xyw}.xim"
  need_file "$EXAMPLES_DIR/$img_basename"
  echo "$img_basename"
}

run_image_capture_stdout() {
  local xyw_bin="$1"
  local img_basename="$2" # e.g. helloworld.xim
  local out_file="$3"

  need_file "$EXAMPLES_DIR/$img_basename"

  local run_log="$TMPDIR/run.${img_basename}.log"
  if ! (cd "$EXAMPLES_DIR" && "$xyw_bin" "$img_basename" >"$out_file" 2>"$run_log"); then
    dump_file_if_exists "Runtime stderr" "$run_log"
    dump_file_if_exists "Runtime stdout" "$out_file"
    fail "Execution failed for $img_basename"
  fi
}

normalize_crlf() {
  local in_file="$1"
  local out_file="$2"

  tr -d '\r' <"$in_file" >"$out_file"
}

### Assertion functions

assert_file_equals() {
  local actual_file="$1"
  local expected_file="$2"

  if ! cmp -s "$actual_file" "$expected_file"; then
    echo "--- Expected (cat -v) ---" >&2
    cat -v "$expected_file" >&2 || true
    echo "--- Actual (cat -v) ---" >&2
    cat -v "$actual_file" >&2 || true
    fail "Output did not match expectation."
  fi
}

assert_output_equals() {
  local stdout_file="$1"
  local expected_string="$2"

  local expected_file
  expected_file="$TMPDIR/expected.$$.${RANDOM}.txt"
  printf '%s' "$expected_string" >"$expected_file"
  assert_file_equals "$stdout_file" "$expected_file"
  rm -f "$expected_file"
}

assert_output_in_range() {
  local stdout_file="$1"
  local min_value="$2"
  local max_value="$3"

  local value
  value="$(tr -d '\n\t ' <"$stdout_file")"

  if [[ ! "$value" =~ ^-?[0-9]+$ ]]; then
    echo "--- Actual (cat -v) ---" >&2
    cat -v "$stdout_file" >&2 || true
    fail "Output is not an integer: '$value'"
  fi

  if (( value < min_value || value > max_value )); then
    echo "--- Actual (cat -v) ---" >&2
    cat -v "$stdout_file" >&2 || true
    fail "Output out of range (expected ${min_value}..${max_value}): $value"
  fi
}

### Test runner

run_test() {
  local src_basename="$1"
  local assert_fn="$2"
  shift 2

  if ! declare -F "$assert_fn" >/dev/null 2>&1; then
    fail "Assertion function not found: $assert_fn"
  fi

  need_file "$EXAMPLES_DIR/$src_basename"

  local img_basename
  img_basename="$(assemble_in_examples_dir "$XYW_BIN" "$src_basename")"

  local raw_out="$TMPDIR/${src_basename%.xyw}.raw"
  local norm_out="$TMPDIR/${src_basename%.xyw}.norm"

  run_image_capture_stdout "$XYW_BIN" "$img_basename" "$raw_out"
  normalize_crlf "$raw_out" "$norm_out"

  "$assert_fn" "$norm_out" "$@"
  echo "-> PASS: $src_basename"
}

### Main

main() {
  need_file "$ROOT_DIR/Makefile"

  build_xyw

  clean_examples_images

  TMPDIR="$(mktemp -d 2>/dev/null || mktemp -d -t xywtest)"
  trap "rm -rf \"$TMPDIR\"" EXIT

  XYW_BIN="$(find_xyw_bin)"

  echo ""

  for tc in "${TEST_CASES[@]}"; do
    eval "$tc"
  done

  echo -e "\n=> All tests passed."
}

main "$@"