Conversation
…function check C09, C10, C11, C12 and C13 had no tests at all before this, 41 exercises across the five modules. C09's libft exercise builds libft.a with the student's own libft_creator.sh script and then links a small harness against it to actually exercise the five functions, since there is no fixed source file name to #include directly. The Makefile exercise works the same way: the subject says the grader supplies srcs/ and includes/ft.h itself, so the test drops that fixture in before driving make/clean/fclean/re and checking that .o files land next to their .c, that clean does not touch libft.a, and so on. C10's four exercises (display_file, cat, tail, hexdump) are also full programs with no fixed file name, so they get built with make and driven through popen the same way C06's argv exercises already were. ft_hexdump -C in particular needed its exact byte layout checked against the real system hexdump across a dozen sizes (empty file, exactly 16 bytes, a partial last line...) before I trusted it enough to bake in as a self-contained reference generator, since I did not want the test to depend on hexdump actually being installed wherever it runs. C11, C12 and C13 came straight from the official subject PDFs; C12 and C13 both need a canonical t_list/t_btree struct, which now lives once in mini-moul/utils/ft_list.h and ft_btree.h instead of being copied into every exercise directory. While building all that I went through this repo's open GitHub issues and fixed what I could actually reproduce. A few were pretty bad. C03/ex05's test built its dest buffer sized to strlen(dest), but ft_strlcat is entitled to assume dest has `size` bytes available, which can be a lot bigger than the initial string - a correct implementation could write past the end of that buffer and segfault or corrupt memory, which lines up with the segfault reports in k11q#29, k11q#35, k11q#41, k11q#20 and k11q#23. C03/ex02 had a sneakier version of the same idea: dest was a fixed-size array inside a struct literal, which C zero-initializes for you, so a student who forgot to null-terminate their ft_strcat result would still pass, because the buffer was already zero from the declaration - that is exactly k11q#11. Fixed it by poisoning the buffer with a non-zero byte before the call so a real omission actually shows up. C03/ex04 had a genuine crash risk too: when ft_strstr correctly returns NULL, the old test passed that straight into printf("%s", ...), which is undefined behavior and can segfault depending on the libc, k11q#29 again. C05/ex08 (ft_ten_queens_puzzle) was still a literal "Sorry, test not implemented yet" stub with a syntax error in it, which is k11q#43's compile error. Past that, the stub expected 92 solutions, which is the answer for eight queens, not ten - ten queens has 724. C02/ex12 was the same kind of stub, referenced in k11q#40. C02/ex09's capitalize test never exercised the subject's own rule that a word is alphanumeric, meaning a digit inside a word blocks capitalization of whatever follows it, which is k11q#31's exact complaint: 123AA should become 123aa, not 123Aa. C02/ex10 never tested size=0 for ft_strlcpy, a classic off-by-one that k11q#42 was presumably running into. C04/ex03's atoi tests only used plain spaces for leading whitespace even though the subject explicitly says isspace(3), and separately asserted a specific result for integer overflow/underflow that the subject explicitly states is undefined, so I removed those two cases rather than pin down behavior the subject itself refuses to define. C05/ex02 and ex03 never tested 0 to the power 0, which the subject explicitly defines as 1 (k11q#27, k11q#28). C05/ex05's ft_sqrt never got an INT_MAX case, and it turns out a naive implementation that just increments i while i*i < nb can overflow and loop forever there, which is k11q#16's timeout complaint. C07/ex04 never tested nbr="0", a case that trips up any implementation that builds its output by dividing until the value hits zero and never enters the loop for an input that is already zero (k11q#15); also fixed the same NULL-into-printf("%s") issue there on its error path. None of the above would have shown up cleanly without three harness bugs I ran into while chasing them, and these affect every exercise, not just the new modules. test.sh's strict compile check was compiling the mini-moul test file itself, which already has its own #include <string.h>, <stdlib.h> and so on, rather than the student's actual source, so a student missing an #include in their own file would still pass - the exact complaint in k11q#33. It now compiles the student's file in isolation with -c so that class of bug cannot hide anymore. Separately, GCC's C23 default changes what an empty-parens function pointer like int (*cmp)() means: it used to mean "unspecified arguments", now it strictly means zero arguments, which broke every C12 exercise that follows the subject's own prototype, since -std was not pinned anywhere; -std=gnu99 fixes that. And test.sh had no timeout anywhere around test execution, so a single hanging student binary - like the ft_sqrt case above - would freeze the whole run forever with no way out. There is now a 10 second timeout around it, and the exit code, which used to always be 0 whether the run passed or failed because of an unrelated printf at the very end, now actually reflects the result. mini-moul.sh itself was worse: it crashed on every single invocation, because run_norminette was called before its own definition, and bash does not hoist function definitions the way some other languages do. While fixing that I also added support for lowercase project folder names (c00 through c13, not just C00-C13, k11q#18) and stopped norminette from scanning the freshly-copied mini-moul/ folder itself, which does not follow the Norm and was never supposed to be graded against it - previously every run buried the actual result under a wall of irrelevant norm errors about the test harness's own code. Last, k11q#30 asked for forbidden-function detection, which did not exist at all before. Every exercise's own subject lists which functions are allowed - most say "None", some say "write", one or two list something like "malloc, free" - and this repo had no way to check that a student was not just calling printf where only write was allowed. Each exercise's mini-moul test file now carries a one-line "// ALLOWED_FUNCTIONS: ..." comment copied straight from that exercise's subject, so the list lives right next to the test it applies to instead of in a separate file that could drift out of sync. mini-moul/utils/ strip_c.c blanks out comments and the contents of string and char literals from the student's source first, so a function name mentioned in a comment or a string does not get flagged by mistake, and mini-moul/utils/check_forbidden.sh greps what is left for calls to a fixed list of the usual suspects (printf, malloc, strcpy, and so on) that are not in that exercise's allowed list. If it finds one, test.sh marks that exercise -42 and forces the whole run's final score to -42/100 with Status: CHEATER, exactly what the real subject says should happen ("Cheaters get -42, and this grade is non-negotiable"). It is a heuristic built on grep, not a real C parser, so it will not catch a function called only through a pointer or anything similarly indirect, but it covers the obvious case people were actually asking about. On top of that reference/broken-implementation check, I cloned two complete, real student piscine repos (C00-C13 all filled in) and ran every module through mini-moul.sh end to end, not just test.sh, treating every failure as either a real student bug to leave alone or a real bug in this harness to fix. That surfaced several more genuine problems. test.sh's isolated compile check was a blind cc -c on whatever .c files sat directly in the exercise folder, which fell over for anyone who organizes their code into srcs/+includes/ and drives it with their own Makefile - C08 and C10 failed wholesale until the check learned to prefer make -s when a Makefile exists, treat zero .c files as fine for header-only exercises (C08 asks for a .h and nothing else), and add -I to the harness's own utils/ directory so a student who doesn't keep a local copy of ft_list.h/ft_btree.h/ft_stock_str.h isn't penalized for it. do-op.c (C11/ex05) had the exact same blind-wildcard problem in its own build step, fixed the same way. check_forbidden.sh's file scan had a real false positive too: it scanned every .c file sitting in the exercise directory, so a student who kept a local copy of an earlier exercise's deliverable in the same folder (to make it compile standalone) got flagged for that file's calls, not their own. It now scans only the specific file the exercise's test #includes, or, for C08's header-only exercises, only the header, falling back to the old wildcard scan solely for the do-op/C10-style programs that genuinely build every .c file in the directory. The most serious one: C09/ex01's Makefile test writes its own srcs/+includes/ fixture over the student's files (since the real subject says the grader supplies them) and deletes it afterward - and it turns out one of the two real repos checks its own srcs/+includes/ into git anyway, so the very first run of this test against it permanently deleted those files. It now backs up whatever was already there before writing the fixture and restores it afterward. Two smaller test crashes came out of the same sweep: C07/ex02's ft_ultimate_range test freed an uninitialized pointer when the student's implementation legitimately never wrote through its output parameter, which crashed the whole test binary instead of just failing that one case; and C07/ex01 printed the wrong array index in its failure message (result[i] instead of result[0]), which was cosmetically confusing but never affected the actual pass/fail logic. Every new or rewritten test in here was checked against both a correct reference implementation and a deliberately broken one before I kept it, including the harness fixes and the forbidden-function detector itself, then validated end to end against two complete real student repos.
|
Thanks a lot for your work ! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
C09, C10, C11, C12 and C13 had no tests at all before this — 41 exercises across the five modules, each one checked against both a correct reference implementation and a deliberately broken one before being kept.
New coverage
libftbuildslibft.avia the student's ownlibft_creator.shscript and links a harness against it;Makefiledrops a grader-suppliedsrcs/+includes/ft.hfixture and drivesmake/clean/fclean/re;ft_splitdisplay_file,cat,tail,hexdump— built withmakeand driven throughpopen, like C06's argv exercises.ft_hexdump -C's byte layout was checked against the real systemhexdumpacross a dozen sizes before being baked in as a self-contained reference generatorft_list.h/ft_btree.hinmini-moul/utils/instead of duplicating the header per exerciseFixes #11, #15, #16, #18, #27, #28, #29, #30, #31, #33, #35, #40, #41, #42, #43
The two segfault-class bugs
ft_strlcat's test builtdestsized tostrlen(dest), but the function is entitled to assumedesthassizebytes available — which can be a lot bigger than the initial string. A correct implementation could write straight past the buffer.ft_strcat's test had the opposite problem:destwas a fixed array inside a struct literal, which C zero-initializes for you — so a student who forgot to null-terminate their result would still pass, since the missing byte was already zero.Everything else that was actually broken
ft_ten_queens_puzzlewas still a stub that didn't even compile, and separately expected 92 solutions (the eight-queens answer) instead of 724ft_strcapitalizenever tested the subject's own rule that a digit inside a word blocks capitalization of what follows:123AA→123aa, not123Aaft_strlcpynever testedsize=0, a classic off-by-oneft_atoionly tested plain spaces even though the subject saysisspace(3), and separately pinned a specific overflow/underflow result the subject explicitly calls undefined — removed those two cases0 ** 0was untested for both power exercises, even though the subject explicitly defines it as1ft_sqrtnever got anINT_MAXcase — a naivewhile (i * i < nb) i++;overflows and loops forever thereft_convert_basenever testednbr="0", which breaks any implementation that divides until zero and never enters the loop for an input already at zeroHarness bugs affecting every exercise, not just the new ones
test.sh's "strict compile" step was compiling its own test file — which already has#include <string.h>etc. — instead of the student's file, so a missing include in the student's own code silently passed:It also had no timeout anywhere around test execution, so one hanging binary (the
ft_sqrtcase above, for instance) froze the entire run forever. There's now a 10 secondtimeoutaround it, and the exit code — which used to always be 0 regardless of pass/fail — now reflects the real result.mini-moul.shcrashed on every single invocation:Bash doesn't hoist function definitions, so this never worked. Fixed, plus lowercase folder name support (
c00-c13, #18) and excludedmini-moul/itself from thenorminettescan — it doesn't follow the Norm and was never meant to be graded against it.New:
-42for forbidden functions (#30)Each exercise's own subject lists which functions are allowed — most say "None". Nothing in this repo checked that a student wasn't just calling
printfwhere onlywritewas allowed. Every exercise's test file now carries that list straight from its subject:// ALLOWED_FUNCTIONS: malloc, freestrip_c.cblanks out comments and string/char literal contents from the student's source first, so a function name mentioned in a comment or a string doesn't get flagged, andcheck_forbidden.shgreps what's left for calls to a fixed list of common libc functions not on that exercise's list. A hit marks the exercise-42and forces the whole run's final score to-42/100,Status: CHEATER— matching the subject's own rule word for word ("Cheaters get -42, and this grade is non-negotiable"). It's grep-based, not a real parser, so it won't catch a function called only through a pointer, but it covers the obvious case.Validated against two complete, real student piscine repos
Checking every new test against a reference and a broken implementation doesn't catch problems that only show up on real, organically-written code. So I cloned two full C00–C13 student repos and ran the actual
mini-moul.shentry point (not justtest.sh) against every module, treating each failure as either a real student bug to leave alone, or a real bug in this harness.test.sh's isolated compile check was a blindcc -con whatever.cfiles sat directly in the exercise folder — anyone who organizes their code intosrcs/+includes/and drives it with their own Makefile failed wholesale. It now prefersmake -swhen a Makefile exists, treats zero.cfiles as fine for header-only exercises (C08 asks for a.hand nothing else), and adds-Ito the harness's ownutils/directory so a student who doesn't keep a local copy offt_list.h/ft_btree.h/ft_stock_str.hisn't penalized for itdo-op.c(C11/ex05) had the same blind-wildcard build problem in its own compile step, fixed the same waycheck_forbidden.shscanned every.cfile sitting in the exercise directory, so a student keeping a local copy of an earlier exercise's deliverable in the same folder (so it compiles standalone) got flagged for that file's calls. It now scans only the specific file the exercise's test#includes — or, for C08's header-only exercises, only the header — and falls back to the old wildcard scan solely for the do-op/C10-style programs that genuinely build every.cfile in the directorysrcs/+includes/fixture over the student's files (the real subject says the grader supplies them) and deletes it afterward. One of the two real repos checks its ownsrcs//includes/into git anyway — the very first run against it permanently deleted those files. It now backs up whatever was already there before writing the fixture and restores it afterward:ft_ultimate_rangetest freed an uninitialized pointer when a student's implementation legitimately never wrote through its output parameter, crashing the whole test binary instead of just failing that case; C07/ex01 printed the wrong array index in its failure message (result[i]instead ofresult[0]) — cosmetic, never affected pass/fail