|
| 1 | +#!/bin/bash |
| 2 | +# |
| 3 | +# compare-macos.sh - Compare our mailx implementation against macOS /usr/bin/mailx |
| 4 | +# |
| 5 | +# This script runs 5 key tests comparing output between the system mailx and our |
| 6 | +# Rust implementation. Run from the mailx/ directory or repository root. |
| 7 | +# |
| 8 | +# Usage: ./mailx/compare-macos.sh |
| 9 | +# |
| 10 | +# Prerequisites: |
| 11 | +# - macOS with /usr/bin/mailx |
| 12 | +# - cargo build completed (debug or release) |
| 13 | +# |
| 14 | + |
| 15 | +# Don't use set -e as we expect some commands to fail (e.g., -e with no mail) |
| 16 | + |
| 17 | +# Colors for output |
| 18 | +RED='\033[0;31m' |
| 19 | +GREEN='\033[0;32m' |
| 20 | +YELLOW='\033[1;33m' |
| 21 | +NC='\033[0m' # No Color |
| 22 | + |
| 23 | +# Find script directory and repo root |
| 24 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 25 | +if [[ -f "$SCRIPT_DIR/Cargo.toml" ]]; then |
| 26 | + MAILX_DIR="$SCRIPT_DIR" |
| 27 | +elif [[ -f "$SCRIPT_DIR/../mailx/Cargo.toml" ]]; then |
| 28 | + MAILX_DIR="$SCRIPT_DIR/../mailx" |
| 29 | +else |
| 30 | + MAILX_DIR="$(pwd)/mailx" |
| 31 | +fi |
| 32 | +REPO_ROOT="$(cd "$MAILX_DIR/.." && pwd)" |
| 33 | + |
| 34 | +# Test data file |
| 35 | +TESTDATA="$MAILX_DIR/tests/cli/testdata.mbox" |
| 36 | + |
| 37 | +# Check prerequisites |
| 38 | +if [[ ! -f /usr/bin/mailx ]]; then |
| 39 | + echo -e "${RED}Error: /usr/bin/mailx not found. This script requires macOS.${NC}" |
| 40 | + exit 1 |
| 41 | +fi |
| 42 | + |
| 43 | +if [[ ! -f "$TESTDATA" ]]; then |
| 44 | + echo -e "${RED}Error: Test data not found at $TESTDATA${NC}" |
| 45 | + exit 1 |
| 46 | +fi |
| 47 | + |
| 48 | +# Find our mailx binary |
| 49 | +if [[ -f "$REPO_ROOT/target/release/mailx" ]]; then |
| 50 | + OUR_MAILX="$REPO_ROOT/target/release/mailx" |
| 51 | +elif [[ -f "$REPO_ROOT/target/debug/mailx" ]]; then |
| 52 | + OUR_MAILX="$REPO_ROOT/target/debug/mailx" |
| 53 | +else |
| 54 | + echo -e "${YELLOW}Building mailx...${NC}" |
| 55 | + (cd "$REPO_ROOT" && cargo build -p posixutils-mailx --bin mailx) |
| 56 | + OUR_MAILX="$REPO_ROOT/target/debug/mailx" |
| 57 | +fi |
| 58 | + |
| 59 | +echo "==============================================" |
| 60 | +echo "mailx Comparison Tests: macOS vs Our Implementation" |
| 61 | +echo "==============================================" |
| 62 | +echo "System mailx: /usr/bin/mailx" |
| 63 | +echo "Our mailx: $OUR_MAILX" |
| 64 | +echo "Test data: $TESTDATA" |
| 65 | +echo "" |
| 66 | + |
| 67 | +PASSED=0 |
| 68 | +FAILED=0 |
| 69 | + |
| 70 | +# Temp files for output comparison |
| 71 | +MACOS_OUT=$(mktemp) |
| 72 | +OUR_OUT=$(mktemp) |
| 73 | +trap "rm -f $MACOS_OUT $OUR_OUT" EXIT |
| 74 | + |
| 75 | +# Helper function to run a test |
| 76 | +run_test() { |
| 77 | + local test_name="$1" |
| 78 | + local description="$2" |
| 79 | + shift 2 |
| 80 | + |
| 81 | + echo -n "Test: $test_name - $description... " |
| 82 | +} |
| 83 | + |
| 84 | +# Helper to show results |
| 85 | +show_result() { |
| 86 | + local result="$1" |
| 87 | + local details="$2" |
| 88 | + |
| 89 | + if [[ "$result" == "pass" ]]; then |
| 90 | + echo -e "${GREEN}PASS${NC}" |
| 91 | + PASSED=$((PASSED + 1)) |
| 92 | + else |
| 93 | + echo -e "${RED}FAIL${NC}" |
| 94 | + if [[ -n "$details" ]]; then |
| 95 | + echo " $details" |
| 96 | + fi |
| 97 | + FAILED=$((FAILED + 1)) |
| 98 | + fi |
| 99 | +} |
| 100 | + |
| 101 | +# Normalize output for comparison (remove timestamps, whitespace differences) |
| 102 | +normalize_output() { |
| 103 | + # Remove leading/trailing whitespace, normalize multiple spaces |
| 104 | + # Remove date/time patterns that vary |
| 105 | + sed -E \ |
| 106 | + -e 's/[A-Z][a-z]{2} [A-Z][a-z]{2} [0-9 ][0-9] [0-9]{2}:[0-9]{2}/DATE_TIME/g' \ |
| 107 | + -e 's/[0-9]{4}-[0-9]{2}-[0-9]{2}/DATE/g' \ |
| 108 | + -e 's/[0-9]+\/[0-9]+/N\/N/g' \ |
| 109 | + -e 's/^[[:space:]]+//' \ |
| 110 | + -e 's/[[:space:]]+$//' \ |
| 111 | + -e 's/[[:space:]]+/ /g' \ |
| 112 | + -e '/^$/d' |
| 113 | +} |
| 114 | + |
| 115 | +echo "----------------------------------------------" |
| 116 | +echo "TEST 1: -e option (check mail presence)" |
| 117 | +echo "----------------------------------------------" |
| 118 | + |
| 119 | +# Test 1a: -e with mail present (should exit 0) |
| 120 | +run_test "1a" "-e with mail present returns exit 0" |
| 121 | +/usr/bin/mailx -n -e -f "$TESTDATA" > /dev/null 2>&1 |
| 122 | +MACOS_EXIT=$? |
| 123 | +"$OUR_MAILX" -n -e -f "$TESTDATA" > /dev/null 2>&1 |
| 124 | +OUR_EXIT=$? |
| 125 | + |
| 126 | +if [[ "$MACOS_EXIT" -eq 0 && "$OUR_EXIT" -eq 0 ]]; then |
| 127 | + show_result "pass" |
| 128 | +else |
| 129 | + show_result "fail" "macOS exit=$MACOS_EXIT, ours exit=$OUR_EXIT (expected both 0)" |
| 130 | +fi |
| 131 | + |
| 132 | +# Test 1b: -e with empty file (should exit non-zero) |
| 133 | +EMPTY_MBOX=$(mktemp) |
| 134 | +run_test "1b" "-e with no mail returns non-zero exit" |
| 135 | +/usr/bin/mailx -n -e -f "$EMPTY_MBOX" > /dev/null 2>&1 |
| 136 | +MACOS_EXIT=$? |
| 137 | +"$OUR_MAILX" -n -e -f "$EMPTY_MBOX" > /dev/null 2>&1 |
| 138 | +OUR_EXIT=$? |
| 139 | +rm -f "$EMPTY_MBOX" |
| 140 | + |
| 141 | +if [[ "$MACOS_EXIT" -ne 0 && "$OUR_EXIT" -ne 0 ]]; then |
| 142 | + show_result "pass" |
| 143 | +else |
| 144 | + show_result "fail" "macOS exit=$MACOS_EXIT, ours exit=$OUR_EXIT (expected both non-zero)" |
| 145 | +fi |
| 146 | + |
| 147 | +echo "" |
| 148 | +echo "----------------------------------------------" |
| 149 | +echo "TEST 2: -H option (headers only)" |
| 150 | +echo "----------------------------------------------" |
| 151 | + |
| 152 | +run_test "2" "-H shows header summary and exits" |
| 153 | +/usr/bin/mailx -n -H -f "$TESTDATA" > "$MACOS_OUT" 2>&1 |
| 154 | +MACOS_EXIT=$? |
| 155 | +"$OUR_MAILX" -n -H -f "$TESTDATA" > "$OUR_OUT" 2>&1 |
| 156 | +OUR_EXIT=$? |
| 157 | + |
| 158 | +# Check both exited successfully |
| 159 | +if [[ "$MACOS_EXIT" -ne 0 || "$OUR_EXIT" -ne 0 ]]; then |
| 160 | + show_result "fail" "Exit codes: macOS=$MACOS_EXIT, ours=$OUR_EXIT" |
| 161 | +else |
| 162 | + # Check both contain the senders |
| 163 | + MACOS_HAS_ALICE=$(grep -ci "alice" "$MACOS_OUT" || true) |
| 164 | + MACOS_HAS_BOB=$(grep -ci "bob" "$MACOS_OUT" || true) |
| 165 | + MACOS_HAS_CHARLIE=$(grep -ci "charlie" "$MACOS_OUT" || true) |
| 166 | + OUR_HAS_ALICE=$(grep -ci "alice" "$OUR_OUT" || true) |
| 167 | + OUR_HAS_BOB=$(grep -ci "bob" "$OUR_OUT" || true) |
| 168 | + OUR_HAS_CHARLIE=$(grep -ci "charlie" "$OUR_OUT" || true) |
| 169 | + |
| 170 | + if [[ "$MACOS_HAS_ALICE" -gt 0 && "$OUR_HAS_ALICE" -gt 0 && \ |
| 171 | + "$MACOS_HAS_BOB" -gt 0 && "$OUR_HAS_BOB" -gt 0 && \ |
| 172 | + "$MACOS_HAS_CHARLIE" -gt 0 && "$OUR_HAS_CHARLIE" -gt 0 ]]; then |
| 173 | + show_result "pass" |
| 174 | + else |
| 175 | + show_result "fail" "Missing senders in output" |
| 176 | + echo " macOS output:" |
| 177 | + head -5 "$MACOS_OUT" | sed 's/^/ /' |
| 178 | + echo " Our output:" |
| 179 | + head -5 "$OUR_OUT" | sed 's/^/ /' |
| 180 | + fi |
| 181 | +fi |
| 182 | + |
| 183 | +echo "" |
| 184 | +echo "----------------------------------------------" |
| 185 | +echo "TEST 3: from command" |
| 186 | +echo "----------------------------------------------" |
| 187 | + |
| 188 | +run_test "3a" "from * shows all message headers" |
| 189 | +echo "from *" | /usr/bin/mailx -n -N -f "$TESTDATA" > "$MACOS_OUT" 2>&1 |
| 190 | +echo "from *" | "$OUR_MAILX" -n -N -f "$TESTDATA" > "$OUR_OUT" 2>&1 |
| 191 | + |
| 192 | +# Count lines with message indicators (should have 3 messages) |
| 193 | +MACOS_LINES=$(grep -ciE "alice|bob|charlie" "$MACOS_OUT" || true) |
| 194 | +OUR_LINES=$(grep -ciE "alice|bob|charlie" "$OUR_OUT" || true) |
| 195 | + |
| 196 | +if [[ "$MACOS_LINES" -ge 3 && "$OUR_LINES" -ge 3 ]]; then |
| 197 | + show_result "pass" |
| 198 | +else |
| 199 | + show_result "fail" "macOS has $MACOS_LINES sender lines, ours has $OUR_LINES" |
| 200 | +fi |
| 201 | + |
| 202 | +run_test "3b" "from 1 shows first message header" |
| 203 | +echo -e "from 1\nquit" | /usr/bin/mailx -n -N -f "$TESTDATA" > "$MACOS_OUT" 2>&1 |
| 204 | +echo -e "from 1\nquit" | "$OUR_MAILX" -n -N -f "$TESTDATA" > "$OUR_OUT" 2>&1 |
| 205 | + |
| 206 | +MACOS_HAS_ALICE=$(grep -ci "alice" "$MACOS_OUT" || true) |
| 207 | +OUR_HAS_ALICE=$(grep -ci "alice" "$OUR_OUT" || true) |
| 208 | + |
| 209 | +if [[ "$MACOS_HAS_ALICE" -gt 0 && "$OUR_HAS_ALICE" -gt 0 ]]; then |
| 210 | + show_result "pass" |
| 211 | +else |
| 212 | + show_result "fail" "Both should show alice" |
| 213 | +fi |
| 214 | + |
| 215 | +echo "" |
| 216 | +echo "----------------------------------------------" |
| 217 | +echo "TEST 4: print/type command" |
| 218 | +echo "----------------------------------------------" |
| 219 | + |
| 220 | +run_test "4a" "print 1 displays first message" |
| 221 | +echo -e "print 1\nquit" | /usr/bin/mailx -n -N -f "$TESTDATA" > "$MACOS_OUT" 2>&1 |
| 222 | +echo -e "print 1\nquit" | "$OUR_MAILX" -n -N -f "$TESTDATA" > "$OUR_OUT" 2>&1 |
| 223 | + |
| 224 | +# Both should contain the message body |
| 225 | +MACOS_HAS_MEETING=$(grep -ci "meeting tomorrow" "$MACOS_OUT" || true) |
| 226 | +OUR_HAS_MEETING=$(grep -ci "meeting tomorrow" "$OUR_OUT" || true) |
| 227 | +MACOS_HAS_FROM=$(grep -c "^From:" "$MACOS_OUT" || true) |
| 228 | +OUR_HAS_FROM=$(grep -c "^From:" "$OUR_OUT" || true) |
| 229 | + |
| 230 | +if [[ "$MACOS_HAS_MEETING" -gt 0 && "$OUR_HAS_MEETING" -gt 0 && \ |
| 231 | + "$MACOS_HAS_FROM" -gt 0 && "$OUR_HAS_FROM" -gt 0 ]]; then |
| 232 | + show_result "pass" |
| 233 | +else |
| 234 | + show_result "fail" "Message content mismatch" |
| 235 | + echo " macOS: meeting=$MACOS_HAS_MEETING, From=$MACOS_HAS_FROM" |
| 236 | + echo " Ours: meeting=$OUR_HAS_MEETING, From=$OUR_HAS_FROM" |
| 237 | +fi |
| 238 | + |
| 239 | +run_test "4b" "type command (alias for print)" |
| 240 | +echo -e "type 2\nquit" | /usr/bin/mailx -n -N -f "$TESTDATA" > "$MACOS_OUT" 2>&1 |
| 241 | +echo -e "type 2\nquit" | "$OUR_MAILX" -n -N -f "$TESTDATA" > "$OUR_OUT" 2>&1 |
| 242 | + |
| 243 | +MACOS_HAS_PROJECT=$(grep -ci "project" "$MACOS_OUT" || true) |
| 244 | +OUR_HAS_PROJECT=$(grep -ci "project" "$OUR_OUT" || true) |
| 245 | + |
| 246 | +if [[ "$MACOS_HAS_PROJECT" -gt 0 && "$OUR_HAS_PROJECT" -gt 0 ]]; then |
| 247 | + show_result "pass" |
| 248 | +else |
| 249 | + show_result "fail" "type command output mismatch" |
| 250 | +fi |
| 251 | + |
| 252 | +echo "" |
| 253 | +echo "----------------------------------------------" |
| 254 | +echo "TEST 5: size command" |
| 255 | +echo "----------------------------------------------" |
| 256 | + |
| 257 | +run_test "5a" "size 1 shows message size" |
| 258 | +echo -e "size 1\nquit" | /usr/bin/mailx -n -N -f "$TESTDATA" > "$MACOS_OUT" 2>&1 |
| 259 | +echo -e "size 1\nquit" | "$OUR_MAILX" -n -N -f "$TESTDATA" > "$OUR_OUT" 2>&1 |
| 260 | + |
| 261 | +# Both should show size output with message number |
| 262 | +MACOS_HAS_SIZE=$(grep -E "1.*[0-9]+" "$MACOS_OUT" | head -1 || true) |
| 263 | +OUR_HAS_SIZE=$(grep -E "1.*[0-9]+" "$OUR_OUT" | head -1 || true) |
| 264 | + |
| 265 | +if [[ -n "$MACOS_HAS_SIZE" && -n "$OUR_HAS_SIZE" ]]; then |
| 266 | + show_result "pass" |
| 267 | + echo " macOS: $MACOS_HAS_SIZE" |
| 268 | + echo " Ours: $OUR_HAS_SIZE" |
| 269 | +else |
| 270 | + show_result "fail" "Size output missing" |
| 271 | +fi |
| 272 | + |
| 273 | +run_test "5b" "size * shows all message sizes" |
| 274 | +echo -e "size *\nquit" | /usr/bin/mailx -n -N -f "$TESTDATA" > "$MACOS_OUT" 2>&1 |
| 275 | +echo -e "size *\nquit" | "$OUR_MAILX" -n -N -f "$TESTDATA" > "$OUR_OUT" 2>&1 |
| 276 | + |
| 277 | +# Count size lines (should be 3) |
| 278 | +MACOS_SIZE_LINES=$(grep -cE "^[[:space:]]*[0-9]+.*[0-9]+" "$MACOS_OUT" || true) |
| 279 | +OUR_SIZE_LINES=$(grep -cE "^[[:space:]]*[0-9]+.*[0-9]+" "$OUR_OUT" || true) |
| 280 | + |
| 281 | +if [[ "$MACOS_SIZE_LINES" -ge 3 && "$OUR_SIZE_LINES" -ge 3 ]]; then |
| 282 | + show_result "pass" |
| 283 | +else |
| 284 | + show_result "fail" "macOS has $MACOS_SIZE_LINES size lines, ours has $OUR_SIZE_LINES" |
| 285 | +fi |
| 286 | + |
| 287 | +echo "" |
| 288 | +echo "==============================================" |
| 289 | +echo "SUMMARY" |
| 290 | +echo "==============================================" |
| 291 | +echo -e "Passed: ${GREEN}$PASSED${NC}" |
| 292 | +echo -e "Failed: ${RED}$FAILED${NC}" |
| 293 | +echo "" |
| 294 | + |
| 295 | +if [[ "$FAILED" -eq 0 ]]; then |
| 296 | + echo -e "${GREEN}All tests passed!${NC}" |
| 297 | + exit 0 |
| 298 | +else |
| 299 | + echo -e "${RED}Some tests failed.${NC}" |
| 300 | + exit 1 |
| 301 | +fi |
0 commit comments