Exercise mycp a basic GNU cp clone
This commit is contained in:
56
exercises/mycp/Makefile
Normal file
56
exercises/mycp/Makefile
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Nome del file eseguibile
|
||||||
|
TARGET = mycp
|
||||||
|
|
||||||
|
# Compilatore
|
||||||
|
CC = gcc
|
||||||
|
|
||||||
|
# Opzioni di compilazione (C11 + warning utili + debug info)
|
||||||
|
CFLAGS = -Wall -Wextra -fsanitize=address -lasan -std=c11 -g
|
||||||
|
|
||||||
|
# File sorgente
|
||||||
|
SRC = main.c
|
||||||
|
|
||||||
|
LOGFILE = test_results.log
|
||||||
|
|
||||||
|
HTMLREPORT = test_report.html
|
||||||
|
|
||||||
|
VALGRIND_OUTPUT = valgrind_output.txt
|
||||||
|
|
||||||
|
.PHONY: all clean clean-tests test
|
||||||
|
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(SRC)
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
test: clean-tests $(TARGET)
|
||||||
|
./test_mycp.sh
|
||||||
|
|
||||||
|
clean-tests:
|
||||||
|
@echo "[CLEANUP] Removing test files and logs..."
|
||||||
|
rm -f \
|
||||||
|
test_input.txt \
|
||||||
|
test_output.txt \
|
||||||
|
bin_input \
|
||||||
|
bin_output \
|
||||||
|
readonly.txt \
|
||||||
|
existing.txt \
|
||||||
|
bigfile \
|
||||||
|
bigfile_copy \
|
||||||
|
*.copy \
|
||||||
|
$(LOGFILE) \
|
||||||
|
$(HTMLREPORT) \
|
||||||
|
$(VALGRIND_OUTPUT)
|
||||||
|
|
||||||
|
clean: clean-tests
|
||||||
|
rm -f $(TARGET)
|
||||||
|
|
||||||
|
report: test
|
||||||
|
./generate_report.sh
|
||||||
|
|
||||||
|
valgrind: $(TARGET)
|
||||||
|
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./mycp test_input.txt test_output.txt
|
||||||
|
|
||||||
|
valgrind-test: $(TARGET)
|
||||||
|
./valgrind_test.sh
|
||||||
|
|
||||||
40
exercises/mycp/generate_report.sh
Executable file
40
exercises/mycp/generate_report.sh
Executable file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
LOG=test_results.log
|
||||||
|
OUT=test_report.html
|
||||||
|
|
||||||
|
echo "[INFO] Generating HTML report..."
|
||||||
|
|
||||||
|
cat <<EOF > "$OUT"
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>mycp Test Report</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: monospace; background: #111; color: #eee; padding: 20px; }
|
||||||
|
.pass { color: #8f8; }
|
||||||
|
.fail { color: #f88; }
|
||||||
|
h1 { color: #0af; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>mycp Test Report</h1>
|
||||||
|
<pre>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
sed \
|
||||||
|
-e 's/✔/<span class="pass">✔<\/span>/g' \
|
||||||
|
-e 's/✘/<span class="fail">✘<\/span>/g' \
|
||||||
|
-e 's/✅/<span class="pass">✅<\/span>/g' \
|
||||||
|
-e 's/❌/<span class="fail">❌<\/span>/g' \
|
||||||
|
"$LOG" >> "$OUT"
|
||||||
|
|
||||||
|
cat <<EOF >> "$OUT"
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "✅ HTML report saved to $OUT"
|
||||||
|
|
||||||
69
exercises/mycp/main.c
Normal file
69
exercises/mycp/main.c
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#define BUFF_LEN 4096
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// Check number of arguments
|
||||||
|
if (argc < 3) {
|
||||||
|
fprintf(stderr, "Usage: %s SOURCE DEST\n", argv[0]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get arguments
|
||||||
|
const char *src = argv[1];
|
||||||
|
const char *dst = argv[2];
|
||||||
|
|
||||||
|
// Open source file in read only mode
|
||||||
|
int srcfd = open(src, O_RDONLY);
|
||||||
|
if (srcfd == -1) {
|
||||||
|
perror("Failed open source file");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open destination file in write mode, if exists trunc file otherwise create a new file
|
||||||
|
int dstfd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (dstfd == -1) {
|
||||||
|
perror("Failed open destination file");
|
||||||
|
if (close(srcfd) == -1) {
|
||||||
|
perror("Failed close source file");
|
||||||
|
}
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int exit_code = EXIT_SUCCESS;
|
||||||
|
char buff[BUFF_LEN]; // reading data buffer
|
||||||
|
ssize_t read_bytes;
|
||||||
|
while ((read_bytes = read(srcfd, buff, BUFF_LEN)) != 0) {
|
||||||
|
if (read_bytes == -1) {
|
||||||
|
// There is an error for reading source file
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
perror("Failed to read source file");
|
||||||
|
exit_code = EXIT_FAILURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write buffer at destination file
|
||||||
|
if (write(dstfd, buff, read_bytes) == -1) {
|
||||||
|
perror("Error to write to destination file");
|
||||||
|
exit_code = EXIT_FAILURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to close files
|
||||||
|
if (close(srcfd) == -1) {
|
||||||
|
perror("Failed to close source file");
|
||||||
|
exit_code = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (close(dstfd) == -1) {
|
||||||
|
perror("Failed to close destination file");
|
||||||
|
exit_code = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
89
exercises/mycp/test_mycp.sh
Executable file
89
exercises/mycp/test_mycp.sh
Executable file
@@ -0,0 +1,89 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
EXEC=./mycp
|
||||||
|
LOGFILE=test_results.log
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
echo "[INFO] Running tests for $EXEC" > "$LOGFILE"
|
||||||
|
echo "[INFO] Date: $(date)" >> "$LOGFILE"
|
||||||
|
echo "===================================" >> "$LOGFILE"
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo "$1" | tee -a "$LOGFILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
check() {
|
||||||
|
DESC="$1"
|
||||||
|
CMD="$2"
|
||||||
|
EXPECT="$3"
|
||||||
|
|
||||||
|
log "[TEST] $DESC"
|
||||||
|
eval "$CMD" > /dev/null 2>&1
|
||||||
|
RET=$?
|
||||||
|
|
||||||
|
if [ "$RET" -eq "$EXPECT" ]; then
|
||||||
|
log " ✔ SUCCESS (exit code $RET)"
|
||||||
|
PASS=$((PASS + 1))
|
||||||
|
else
|
||||||
|
log " ✘ FAIL (exit code $RET, expected $EXPECT)"
|
||||||
|
FAIL=$((FAIL + 1))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
compare_files() {
|
||||||
|
FILE1="$1"
|
||||||
|
FILE2="$2"
|
||||||
|
if cmp -s "$FILE1" "$FILE2"; then
|
||||||
|
log " ✔ Files are identical"
|
||||||
|
else
|
||||||
|
log " ✘ Files differ!"
|
||||||
|
FAIL=$((FAIL + 1))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test 1
|
||||||
|
echo "Hello World" > test_input.txt
|
||||||
|
check "Copy text file" "$EXEC test_input.txt test_output.txt" 0
|
||||||
|
compare_files test_input.txt test_output.txt
|
||||||
|
|
||||||
|
# Test 2
|
||||||
|
cp /bin/ls bin_input
|
||||||
|
check "Copy binary file" "$EXEC bin_input bin_output" 0
|
||||||
|
compare_files bin_input bin_output
|
||||||
|
|
||||||
|
# Test 3
|
||||||
|
check "Source file does not exist" "$EXEC nofile.txt shouldnotexist" 1
|
||||||
|
|
||||||
|
# Test 4
|
||||||
|
check "Missing arguments" "$EXEC" 1
|
||||||
|
|
||||||
|
# Test 5
|
||||||
|
echo "readonly" > readonly.txt
|
||||||
|
chmod 444 readonly.txt
|
||||||
|
check "Write to read-only file" "$EXEC test_input.txt readonly.txt" 1
|
||||||
|
chmod 644 readonly.txt
|
||||||
|
|
||||||
|
# Test 6
|
||||||
|
echo "old content" > existing.txt
|
||||||
|
check "Overwrite existing file" "$EXEC test_input.txt existing.txt" 0
|
||||||
|
compare_files test_input.txt existing.txt
|
||||||
|
|
||||||
|
# Test 7
|
||||||
|
dd if=/dev/urandom of=bigfile bs=1K count=128 status=none
|
||||||
|
check "Copy large file (128KB)" "$EXEC bigfile bigfile_copy" 0
|
||||||
|
compare_files bigfile bigfile_copy
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
log ""
|
||||||
|
log "Tests passed: $PASS"
|
||||||
|
log "Tests failed: $FAIL"
|
||||||
|
|
||||||
|
if [ "$FAIL" -eq 0 ]; then
|
||||||
|
log "✅ All tests passed"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
log "❌ Some tests failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
19
exercises/mycp/valgrind_test.sh
Executable file
19
exercises/mycp/valgrind_test.sh
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
EXEC=./mycp
|
||||||
|
LOGFILE=valgrind_output.txt
|
||||||
|
INPUT=test_input.txt
|
||||||
|
OUTPUT=test_output.txt
|
||||||
|
|
||||||
|
echo "Testing with Valgrind..." > "$LOGFILE"
|
||||||
|
echo "Input: $INPUT → Output: $OUTPUT" >> "$LOGFILE"
|
||||||
|
echo "----------------------------------------" >> "$LOGFILE"
|
||||||
|
|
||||||
|
# Prepara input file
|
||||||
|
echo "Valgrind test content" > "$INPUT"
|
||||||
|
|
||||||
|
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes \
|
||||||
|
"$EXEC" "$INPUT" "$OUTPUT" 2>> "$LOGFILE"
|
||||||
|
|
||||||
|
echo "✅ Valgrind test completed. Output in $LOGFILE."
|
||||||
|
|
||||||
Reference in New Issue
Block a user