diff --git a/autotest.sh b/autotest.sh new file mode 100755 index 0000000..2ced84a --- /dev/null +++ b/autotest.sh @@ -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 +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 +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 +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 diff --git a/sample_program.c b/sample_program.c new file mode 100644 index 0000000..546cbbf --- /dev/null +++ b/sample_program.c @@ -0,0 +1,61 @@ +#include +#include + +// 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; +}