add sample test program and autotest script

This commit is contained in:
Gabriel Ionita 2025-10-28 09:00:00 +01:00
parent 52710cf7d2
commit 5eec86114a
Signed by: gabi
SSH Key Fingerprint: SHA256:mrbYmB/SGtDvT3etRoS6pDrMYWxR0/B5GSF6rR3qhhc
2 changed files with 333 additions and 0 deletions

272
autotest.sh Executable file
View File

@ -0,0 +1,272 @@
#!/bin/bash
# autotest.sh - Automated testing script for gwatch
# Tests various scenarios and validates behavior
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Test counters
TESTS_PASSED=0
TESTS_FAILED=0
# Paths
GWATCH="./build/gwatch"
SAMPLE_PROGRAM="./sample_program"
SAMPLE_SOURCE="./sample_program.c"
# Helper function to print test results
print_result() {
local test_name="$1"
local result="$2"
if [ "$result" = "PASS" ]; then
echo -e "${GREEN}[PASS]${NC} $test_name"
((TESTS_PASSED++))
else
echo -e "${RED}[FAIL]${NC} $test_name"
((TESTS_FAILED++))
fi
}
# Helper function to run test
run_test() {
local test_name="$1"
echo -e "\n${YELLOW}Running:${NC} $test_name"
}
# Compile sample program
echo "=== Preparing Test Environment ==="
if [ ! -f "$SAMPLE_SOURCE" ]; then
echo "Error: $SAMPLE_SOURCE not found"
exit 1
fi
echo "Compiling sample program..."
gcc -g -O0 -o "$SAMPLE_PROGRAM" "$SAMPLE_SOURCE"
if [ $? -ne 0 ]; then
echo "Error: Failed to compile sample program"
exit 1
fi
echo "Sample program compiled successfully"
if [ ! -f "$GWATCH" ]; then
echo "Error: $GWATCH not found. Build the project first."
exit 1
fi
echo -e "\n=== Starting Tests ===\n"
# Test 1: Monitor global_int with successful writes
run_test "Test 1: Monitor global_int (4 bytes)"
OUTPUT=$(timeout 3 $GWATCH --var global_int --exec $SAMPLE_PROGRAM 2>&1)
EXIT_CODE=$?
# timeout returns 124 if killed, but gwatch itself returns 0
if echo "$OUTPUT" | grep -q "global_int.*write.*0->42" && echo "$OUTPUT" | grep -q "Process exited"; then
print_result "Test 1" "PASS"
else
print_result "Test 1" "FAIL"
echo "Exit code: $EXIT_CODE"
fi
# Test 2: Monitor global_long (8 bytes)
run_test "Test 2: Monitor global_long (8 bytes)"
OUTPUT=$(timeout 3 $GWATCH --var global_long --exec $SAMPLE_PROGRAM 2>&1)
EXIT_CODE=$?
if echo "$OUTPUT" | grep -q "global_long.*write.*1000->5000" && echo "$OUTPUT" | grep -q "Process exited"; then
print_result "Test 2" "PASS"
else
print_result "Test 2" "FAIL"
fi
# Test 3: Monitor global_uint (unsigned 4 bytes)
run_test "Test 3: Monitor global_uint (unsigned 4 bytes)"
OUTPUT=$(timeout 3 $GWATCH --var global_uint --exec $SAMPLE_PROGRAM 2>&1)
EXIT_CODE=$?
if echo "$OUTPUT" | grep -q "global_uint.*write.*0->999" && echo "$OUTPUT" | grep -q "Process exited"; then
print_result "Test 3" "PASS"
else
print_result "Test 3" "FAIL"
fi
# Test 4: Nonexistent variable
run_test "Test 4: Nonexistent variable (should fail)"
OUTPUT=$($GWATCH --var nonexistent_var --exec $SAMPLE_PROGRAM 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ] && echo "$OUTPUT" | grep -q "not found"; then
print_result "Test 4" "PASS"
else
print_result "Test 4" "FAIL"
echo "Exit code: $EXIT_CODE, Output: $OUTPUT"
fi
# Test 5: Missing executable
run_test "Test 5: Missing executable (should fail)"
OUTPUT=$($GWATCH --var global_int --exec /nonexistent/file 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ] && echo "$OUTPUT" | grep -q "Cannot open"; then
print_result "Test 5" "PASS"
else
print_result "Test 5" "FAIL"
echo "Exit code: $EXIT_CODE, Output: $OUTPUT"
fi
# Test 6: Missing arguments
run_test "Test 6: Missing required arguments (should fail)"
OUTPUT=$($GWATCH 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ] && echo "$OUTPUT" | grep -q "required"; then
print_result "Test 6" "PASS"
else
print_result "Test 6" "FAIL"
echo "Exit code: $EXIT_CODE, Output: $OUTPUT"
fi
# Test 7: Help message
run_test "Test 7: Help message"
OUTPUT=$($GWATCH --help 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ] && echo "$OUTPUT" | grep -q "exec" && echo "$OUTPUT" | grep -q "var"; then
print_result "Test 7" "PASS"
else
print_result "Test 7" "FAIL"
echo "Exit code: $EXIT_CODE, Output: $OUTPUT"
fi
# Test 8: Command-line argument passing
run_test "Test 8: Command-line arguments to traced program"
OUTPUT=$(timeout 3 $GWATCH --var global_int --exec $SAMPLE_PROGRAM -- 777 2>&1)
EXIT_CODE=$?
if echo "$OUTPUT" | grep -q "777" && echo "$OUTPUT" | grep -q "Process exited"; then
print_result "Test 8" "PASS"
else
print_result "Test 8" "FAIL"
fi
# Test 9: Unsupported type (char - 1 byte)
run_test "Test 9: Unsupported type - char (1 byte, should fail)"
cat > /tmp/test_char_gwatch.c <<'EOF'
#include <stdio.h>
char global_char = 'A';
int main() {
global_char = 'B';
printf("Char: %c\n", global_char);
return 0;
}
EOF
gcc -g -O0 -o /tmp/test_char_gwatch /tmp/test_char_gwatch.c 2>/dev/null
OUTPUT=$($GWATCH --var global_char --exec /tmp/test_char_gwatch 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ] && echo "$OUTPUT" | grep -q "unsupported size"; then
print_result "Test 9" "PASS"
else
print_result "Test 9" "FAIL"
echo "Exit code: $EXIT_CODE, Output: $OUTPUT"
fi
# Test 10: Unsupported type (short - 2 bytes)
run_test "Test 10: Unsupported type - short (2 bytes, should fail)"
cat > /tmp/test_short_gwatch.c <<'EOF'
#include <stdio.h>
short global_short = 10;
int main() {
global_short = 20;
printf("Short: %d\n", global_short);
return 0;
}
EOF
gcc -g -O0 -o /tmp/test_short_gwatch /tmp/test_short_gwatch.c 2>/dev/null
OUTPUT=$($GWATCH --var global_short --exec /tmp/test_short_gwatch 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ] && echo "$OUTPUT" | grep -q "unsupported size"; then
print_result "Test 10" "PASS"
else
print_result "Test 10" "FAIL"
echo "Exit code: $EXIT_CODE, Output: $OUTPUT"
fi
# Test 11: Unsupported type (float)
run_test "Test 11: Unsupported type - float (should fail)"
cat > /tmp/test_float_gwatch.c <<'EOF'
#include <stdio.h>
float global_float = 3.14f;
int main() {
global_float = 6.28f;
printf("Float: %f\n", global_float);
return 0;
}
EOF
gcc -g -O0 -o /tmp/test_float_gwatch /tmp/test_float_gwatch.c 2>/dev/null
OUTPUT=$($GWATCH --var global_float --exec /tmp/test_float_gwatch 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -ne 0 ] && echo "$OUTPUT" | grep -q "not found or is not a supported integer type"; then
print_result "Test 11" "PASS"
else
print_result "Test 11" "FAIL"
echo "Exit code: $EXIT_CODE, Output: $OUTPUT"
fi
# Test 12: Output format validation (tab-delimited)
run_test "Test 12: Output format validation (tab-delimited)"
OUTPUT=$(timeout 3 $GWATCH --var global_int --exec $SAMPLE_PROGRAM 2>&1)
EXIT_CODE=$?
# Check if output contains tabs between fields
if echo "$OUTPUT" | grep -P "global_int\twrite\t\d+->\d+" >/dev/null && echo "$OUTPUT" | grep -q "Process exited"; then
print_result "Test 12" "PASS"
else
print_result "Test 12" "FAIL"
fi
# Test 13: Multiple writes detection
run_test "Test 13: Multiple writes detection"
OUTPUT=$(timeout 3 $GWATCH --var global_int --exec $SAMPLE_PROGRAM 2>&1)
EXIT_CODE=$?
# Count number of write events (should be at least 5)
WRITE_COUNT=$(echo "$OUTPUT" | grep -c "global_int.*write" || true)
if [ "$WRITE_COUNT" -ge 5 ] && echo "$OUTPUT" | grep -q "Process exited"; then
print_result "Test 13" "PASS"
else
print_result "Test 13" "FAIL"
echo "Expected at least 5 writes, got $WRITE_COUNT"
fi
# Cleanup
rm -f /tmp/test_char_gwatch /tmp/test_char_gwatch.c
rm -f /tmp/test_short_gwatch /tmp/test_short_gwatch.c
rm -f /tmp/test_float_gwatch /tmp/test_float_gwatch.c
# Summary
echo -e "\n=== Test Summary ==="
echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}"
echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}"
echo -e "Total tests: $((TESTS_PASSED + TESTS_FAILED))"
if [ $TESTS_FAILED -eq 0 ]; then
echo -e "\n${GREEN}All tests passed!${NC}"
exit 0
else
echo -e "\n${RED}Some tests failed!${NC}"
exit 1
fi

61
sample_program.c Normal file
View File

@ -0,0 +1,61 @@
#include <stdio.h>
#include <stdlib.h>
// Global variables for testing
volatile int global_int = 0;
volatile long global_long = 1000L;
volatile unsigned int global_uint = 0U;
int main(int argc, char *argv[]) {
printf("=== Sample Program for gwatch ===\n");
// Test 1: Simple write
printf("Test 1: Simple write\n");
global_int = 42;
printf(" global_int = %d\n", global_int);
// Test 2: Multiple writes
printf("Test 2: Multiple writes\n");
global_int = 100;
printf(" global_int = %d\n", global_int);
global_int = 200;
printf(" global_int = %d\n", global_int);
// Test 3: Increment operation
printf("Test 3: Increment\n");
global_int++;
printf(" global_int = %d\n", global_int);
// Test 4: Arithmetic
printf("Test 4: Arithmetic\n");
global_int = global_int + 50;
printf(" global_int = %d\n", global_int);
// Test 5: Read operation (shouldn't show write)
printf("Test 5: Read operation\n");
int temp = global_int;
printf(" Read value: %d\n", temp);
// Test 6: Long variable operations
printf("Test 6: Long variable\n");
global_long = 5000L;
printf(" global_long = %ld\n", global_long);
global_long += 2500L;
printf(" global_long = %ld\n", global_long);
// Test 7: Unsigned int operations
printf("Test 7: Unsigned int\n");
global_uint = 999U;
printf(" global_uint = %u\n", global_uint);
// Test 8: Command-line argument handling
if (argc > 1) {
printf("Test 8: With command-line args\n");
int arg_val = atoi(argv[1]);
global_int = arg_val;
printf(" global_int = %d (from arg)\n", global_int);
}
printf("=== Program Complete ===\n");
return 0;
}