From 30d371e266165b51ac0ecb6e9909ea2ea28f452f Mon Sep 17 00:00:00 2001 From: supercoolin <colin.evrard@uclouvain.be> Date: Thu, 22 Jun 2023 16:07:39 +0200 Subject: [PATCH] Add tests data --- .gitignore | 2 + __init__.py | 0 __pycache__/task_common.cpython-38.pyc | Bin 0 -> 8962 bytes run_tests.py | 11 + task_common.py | 47 +-- tests/__init__.py | 0 tests/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 150 bytes .../__pycache__/test_task_data.cpython-38.pyc | Bin 0 -> 1383 bytes tests/data/tasks/strcpy/run | 145 +++++++ tests/data/tasks/strcpy/solutions/sol.c | 13 + .../tasks/strcpy/student/CTester/CTester.c | 353 ++++++++++++++++++ .../tasks/strcpy/student/CTester/CTester.h | 43 +++ .../data/tasks/strcpy/student/CTester/trap.c | 37 ++ .../data/tasks/strcpy/student/CTester/trap.h | 8 + .../data/tasks/strcpy/student/CTester/wrap.h | 163 ++++++++ .../tasks/strcpy/student/CTester/wrap_file.c | 239 ++++++++++++ .../tasks/strcpy/student/CTester/wrap_file.h | 120 ++++++ .../strcpy/student/CTester/wrap_getpid.c | 42 +++ .../strcpy/student/CTester/wrap_getpid.h | 16 + .../strcpy/student/CTester/wrap_malloc.c | 206 ++++++++++ .../strcpy/student/CTester/wrap_malloc.h | 100 +++++ .../tasks/strcpy/student/CTester/wrap_mutex.c | 117 ++++++ .../tasks/strcpy/student/CTester/wrap_mutex.h | 62 +++ .../tasks/strcpy/student/CTester/wrap_sleep.c | 51 +++ .../tasks/strcpy/student/CTester/wrap_sleep.h | 16 + tests/data/tasks/strcpy/student/Makefile | 41 ++ .../strcpy/student/fr/LC_MESSAGES/tests.mo | Bin 0 -> 901 bytes .../tasks/strcpy/student/student_code.c.tpl | 8 + .../data/tasks/strcpy/student/student_code.h | 5 + tests/data/tasks/strcpy/student/tests.c | 94 +++++ tests/data/tasks/strcpy/task.yaml | 50 +++ tests/data/tasks/strcpy/test/submission1.test | 23 ++ tests/test_task_data.py | 28 ++ 33 files changed, 2017 insertions(+), 23 deletions(-) create mode 100644 .gitignore create mode 100644 __init__.py create mode 100644 __pycache__/task_common.cpython-38.pyc create mode 100644 run_tests.py create mode 100644 tests/__init__.py create mode 100644 tests/__pycache__/__init__.cpython-38.pyc create mode 100644 tests/__pycache__/test_task_data.cpython-38.pyc create mode 100644 tests/data/tasks/strcpy/run create mode 100644 tests/data/tasks/strcpy/solutions/sol.c create mode 100644 tests/data/tasks/strcpy/student/CTester/CTester.c create mode 100644 tests/data/tasks/strcpy/student/CTester/CTester.h create mode 100644 tests/data/tasks/strcpy/student/CTester/trap.c create mode 100644 tests/data/tasks/strcpy/student/CTester/trap.h create mode 100644 tests/data/tasks/strcpy/student/CTester/wrap.h create mode 100644 tests/data/tasks/strcpy/student/CTester/wrap_file.c create mode 100644 tests/data/tasks/strcpy/student/CTester/wrap_file.h create mode 100644 tests/data/tasks/strcpy/student/CTester/wrap_getpid.c create mode 100644 tests/data/tasks/strcpy/student/CTester/wrap_getpid.h create mode 100644 tests/data/tasks/strcpy/student/CTester/wrap_malloc.c create mode 100644 tests/data/tasks/strcpy/student/CTester/wrap_malloc.h create mode 100644 tests/data/tasks/strcpy/student/CTester/wrap_mutex.c create mode 100644 tests/data/tasks/strcpy/student/CTester/wrap_mutex.h create mode 100644 tests/data/tasks/strcpy/student/CTester/wrap_sleep.c create mode 100644 tests/data/tasks/strcpy/student/CTester/wrap_sleep.h create mode 100644 tests/data/tasks/strcpy/student/Makefile create mode 100644 tests/data/tasks/strcpy/student/fr/LC_MESSAGES/tests.mo create mode 100644 tests/data/tasks/strcpy/student/student_code.c.tpl create mode 100644 tests/data/tasks/strcpy/student/student_code.h create mode 100644 tests/data/tasks/strcpy/student/tests.c create mode 100644 tests/data/tasks/strcpy/task.yaml create mode 100644 tests/data/tasks/strcpy/test/submission1.test create mode 100644 tests/test_task_data.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc3b136 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +LSINF1252/ +venv/ \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/__pycache__/task_common.cpython-38.pyc b/__pycache__/task_common.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad8ee7ac5894589d3e36a4ce2c718dd76fb65eaa GIT binary patch literal 8962 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 SscQMc^o7cWsS8u*>^}oJr~~)_ literal 0 HcmV?d00001 diff --git a/run_tests.py b/run_tests.py new file mode 100644 index 0000000..788263b --- /dev/null +++ b/run_tests.py @@ -0,0 +1,11 @@ +import unittest +from tests.test_task_data import TaskDataTestCase + +def suite(): + suite = unittest.TestSuite() + suite.addTest(TaskDataTestCase('test_task_dir_to_TaskData')) + return suite + +if __name__ == '__main__': + runner = unittest.TextTestRunner() + runner.run(suite()) \ No newline at end of file diff --git a/task_common.py b/task_common.py index 49a156f..4859cfc 100644 --- a/task_common.py +++ b/task_common.py @@ -2,13 +2,13 @@ from typing import Optional, List, Callable, Any from pathlib import Path import yaml import os -import subprocess, shlex, re, os, yaml -from inginious import feedback, rst, input import logging import subprocess, shlex, re, os, yaml -LOG_FMT = '%(asctime)s %(message)s' -logging.basicConfig(format=FORMAT) +from dataclasses import dataclass +from itertools import chain +logging.basicConfig() logger = logging.getLogger('JudgeAPI') +logger.setLevel('DEBUG') """ Task common, the file defining the API that will be used for the C judge system. @@ -56,29 +56,28 @@ def task_dir_validate(task_dir_path: Path) -> bool: return False #list task files dir_content = os.listdir(task_dir_path) - dir_files = {str(f).lower():f for f in dir_content if os.path.isfile(f)} - dir_subdirs = {str(d).lower():d for d in dir_content if os.path.isdir(d)} + dir_files = {str(f).lower():f for f in dir_content if os.path.isfile(os.path.join(task_dir_path, f))} + dir_subdirs = {str(d).lower():d for d in dir_content if os.path.isdir(os.path.join(task_dir_path, d))} #check for mandatory files and subdirectories if "task.yaml" not in dir_files and "task.yml" not in dir_files: - logger.debug(f"Could not find task.yaml in {str(task_dir_path)}") + logger.warning(f"Could not find task.yaml in {str(task_dir_path)}") return False if "run" not in dir_files: - logger.debug(f"Could not find run file in {task_dir_path}") + logger.warning(f"Could not find run file in {task_dir_path}") return False if "student" not in dir_subdirs: - logger.debug(f"Could not find student folder in {task_dir_path}. Maybe the tasks are too simple for using this API ?") + logger.warning(f"Could not find student folder in {task_dir_path}. Maybe the tasks are too simple for using this API ?") return False #check the content of the student directory - student_dir = os.path.join(task_dir_path, dir_subdirs['student']) - student_dir_content_splitted = [str(c).to_lower().split('.') for c in os.listdir(student_dir)] - student_dir_content_ext = [splitted[1] for c in student_dir_content_splitted if len(splitted) > 1] - if 'tpl' not in student_dir_content_ext: - logger.debug(f"Could not find template file in {str(student_dir)}") + student_dir_content_splitted = [str(c).lower().split('.') for c in os.listdir(student_dir)] + student_dir_content_ext = [splitted[1:] for splitted in student_dir_content_splitted if len(splitted) > 1] + if 'tpl' not in chain(*student_dir_content_ext): + logger.warning(f"Could not find template file in {str(student_dir)}") return False logger.debug(f"Validated task directory {str(task_dir_path)}") @@ -123,18 +122,20 @@ def task_dir_to_TaskData(task_dir_path: Path, build_script: Path=None, lib_dirs: #discover files and folders task_dir_content = {str(content).lower():content for content in os.listdir(task_dir_path)} - task_dir_files = {name:path for name, path in task_dir_content.items() if os.path.isfile(path)} - task_dir_subdirs = {name:path for name, path in task_dir_content.items() if os.path.isdir(s)(path)} + task_dir_files = {name:path for name, path in task_dir_content.items() if os.path.isfile(os.path.join(task_dir_path, path))} + task_dir_subdirs = {name:path for name, path in task_dir_content.items() if os.path.isdir(os.path.join(task_dir_path, path))} #Guess the yaml extension to the task file and load it into a dict task_file = task_dir_files['task.yaml'] if 'task.yaml' in task_dir_files else task_dir_files['task.yml'] - with open(task_file, 'r') as f: - TaskData_init_kwargs['task'] = yaml.load(f) + with open(os.path.join(task_dir_path, task_file), 'r') as f: + TaskData_init_kwargs['task'] = yaml.safe_load(f) + student_dir_path = os.path.join(task_dir_path, task_dir_content['student']) + #discover files and folders of the student directory - student_dir_content = {str(content).lower():content for content in os.listdir(os.path.join(task_dir_path, task_dir_content['student']))} - student_dir_files = {name:path for name, path in student_dir_content.items() if os.path.isfile(path)} - student_dir_subdirs = {name:path for name, path in student_dir_content.items() if os.path.isdir(s)(path)} + student_dir_content = {str(content).lower():content for content in os.listdir(student_dir_path)} + student_dir_files = {name:path for name, path in student_dir_content.items() if os.path.isfile(os.path.join(student_dir_path, path))} + student_dir_subdirs = {name:path for name, path in student_dir_content.items() if os.path.isfile(os.path.join(student_dir_path, path))} #retrieve the template file and the annex files annex = [] @@ -155,12 +156,12 @@ def task_dir_to_TaskData(task_dir_path: Path, build_script: Path=None, lib_dirs: return TaskData(**TaskData_init_kwargs) -def student_code_generate(task: TaskData): +def student_code_generate(task: TaskData, generator: Callable[[str, str], None]): filename = task.template.name filename_components = filename.split(".") extension = filename_components[1] if len(filename_components) > 2 else "" output_name = f"{filename_components[0]}{extension}" - input.parse_template(filename, output_name) + generator(filename, output_name) task.student_code = output_name logger.debug(f"Generated student code: {output_name}") diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/__pycache__/__init__.cpython-38.pyc b/tests/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f908614706e23bedbec580533cbd7c8575d1e496 GIT binary patch literal 150 zcmWIL<>g`kf=-z!DIoeWh(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vSKO;XkRX;gD zCo@mKAR|S$peR2pHMyi%-_y_CGcPm0v{*N*G$lQ?SidB-7$_VcpP83g5+AQuP<e~P SCO1E&G$+*#WawugW&i;7Z6g-| literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_task_data.cpython-38.pyc b/tests/__pycache__/test_task_data.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..21f1a7a49717f502c4d89f3213595c6441483bb1 GIT binary patch literal 1383 zcmZ`&&2Aev5GI$q+LbKHvaO_c(Sv$afLN#Af*^$h1U^&<Q2RolAT}CCUS<D6Qb29U zC+FH*Z*76-*!SpD@Y+*fp+M2jtfV$>Q!e-|Ih-Nqo1e>GufuTI%kSZfkg;EB^WmV= zXE^OI2*7~ntl*Ltfebk9gIpA$44bvcBN?Gg<Sp51{;`a8YmvNf>v++D@PsY))?9WQ zSq~y;tpnND;byP<5JR#Scl^Et+R#}?ACc~nbk3psqnLBK4?XC^J`CUh4(Ba7c){QZ zhVK{*b+kCR6Y>zm*KGXYH)O)bfln?{`{sG-Qa^OKQji(tDz%f2xes5a?rQ22rHV8w zmGY6()>$6{Sw&=JWy?A<tFL8j-4b-^Wb#$|M!(K-E#tEb)YoP#d}yl5`RLU$%fW|v z_R9Li>7ve4r+r8_`EGNl?961f^9kyZOV&qeS?X_PcYD8@RiJ0Y;lmN&<{3`=8G>e# z1Cs%82;MO%KtPE95F+H?@`D{k^6o6PddAUcH)u`s45!7Cz$~K`pZ<*CL#y-G<`AU~ z{Ql+Ssw(tkR^?easjuL1ZK{QyIXgL@zBn(lYH1%YmN3_L(uA%-MRXO#@U*`6k=5?9 zen77g3C3?8{@cIPM&+KD<X8)5<K+b|9M<LuyXB6rko5^e6b)k4-HF>^#oh<2V9l{o zqv!0K(XCjq1xglTg?i9hi60omKM6>_!^(bp+OTd<4TZwA38)p!9>O>@JzDn(_6Y_E zH|>|Es<m;~qnpksv!iKM>e1&IG*Q_3IP~oeZA_^VsPFevYf=5&PAf;cRA-RQoKpC} z5o*X}XCb$?7%~}Ri0gk+V1{(p2}O4s*ZXCBjK{X=ah~vF-a)*1^q&C#ulaNwkzZw6 zXsrJZc0vWqoYq~X-Y(O8b7Cle=2L>di(|7Q_sjtTF>2~52@=sr#z%6mVK}RbqACrg ze-oOx#>T@2FqCGWEX&N1hd!vR7xo%EkQ983{Xy^hc;glJlv31rO3CiF5>?Z9;iAe) z*>7l4;VH5=pWyBCXcuHffyIPm8t=K-wnpRV$2j@q$>!0gR3VnA4|o@&OW%<9#UX#l MkN*h&43mNQ3;9iJIsgCw literal 0 HcmV?d00001 diff --git a/tests/data/tasks/strcpy/run b/tests/data/tasks/strcpy/run new file mode 100644 index 0000000..091a760 --- /dev/null +++ b/tests/data/tasks/strcpy/run @@ -0,0 +1,145 @@ +#!/bin/python3 + +# Script d'interface entre INGInious et des tests unitaires écrits à l'aide de CUnit +# Auteurs : Mathieu Xhonneux, Anthony Gégo +# Licence : GPLv3 + +import subprocess, shlex, re, os, yaml +from inginious import feedback, rst, input + +# Switch working directory to student/ +os.chdir("student") + +# Fetch and save the student code into a file for compilation +input.parse_template("student_code.c.tpl", "student_code.c") + +# Compilation +p = subprocess.Popen(shlex.split("make"), stderr=subprocess.STDOUT, stdout=subprocess.PIPE) +make_output = p.communicate()[0].decode('utf-8') +# If compilation failed, exit with "failed" result +if p.returncode: + feedback.set_tag("not_compile", True) + feedback.set_global_result("failed") + feedback.set_global_feedback("La compilation de votre code a échoué. Voici le message de sortie de la commande ``make`` :") + feedback.set_global_feedback(rst.get_codeblock('', make_output), True) + exit(0) +else: + # Cppcheck + p = subprocess.Popen(shlex.split("make check"), stderr=subprocess.STDOUT, stdout=subprocess.PIPE) + cppcheck_output = p.communicate()[0].decode('utf-8') + if p.returncode: + feedback.set_tag("cppcheck", True) + feedback.set_global_result("failed") + feedback.set_global_feedback("La compilation de votre code avec ``cppcheck`` a échoué. Voici le message de sortie de la commande ``make check`` :") + feedback.set_global_feedback(rst.get_codeblock('', cppcheck_output), True) + exit(0) + else: + feedback.set_global_result("success") + feedback.set_global_feedback("- Votre code compile.\n") + +# Parse banned functions +try: + banned_funcs = re.findall("BAN_FUNCS\(([a-zA-Z0-9_, ]*)\)", open('tests.c').read())[-1].replace(" ", "").split(",") + banned_funcs = list(filter(None, banned_funcs)) +except IndexError: + banned_funcs = [] + +if banned_funcs: + p = subprocess.Popen(shlex.split("readelf -s student_code.o"), stderr=subprocess.STDOUT, stdout=subprocess.PIPE) + readelf_output = p.communicate()[0].decode('utf-8') + for func in banned_funcs: + if re.search("UND {}\n".format(func), readelf_output): + feedback.set_tag("banned_funcs", True) + feedback.set_global_result("failed") + feedback.set_global_feedback("Vous utilisez la fonction {}, qui n'est pas autorisée.".format(func)) + exit(0) + + +# Remove source files +subprocess.run("rm -rf *.c *.tpl *.h *.o", shell=True) + +LANG = input.get_input('@lang') + +# Run the code in a parallel container +p = subprocess.Popen(shlex.split("run_student --time 20 --hard-time 60 ./tests LANGUAGE={}".format(LANG)), stderr=subprocess.STDOUT, stdout=subprocess.PIPE) +o, e = p.communicate() +print(o.decode("utf-8")) +# If run failed, exit with "failed" result +if p.returncode: + feedback.set_global_result("failed") + if p.returncode == 256-8: + montest_output = rst.get_admonition("warning", "**Erreur d'exécution**", "Votre code a produit une erreur. Le signal SIGFPE a été envoyé : *Floating Point Exception*.") + feedback.set_tag("sigfpe", True) + elif p.returncode == 256-11: + montest_output = rst.get_admonition("warning", "**Erreur d'exécution**", "Votre code a produit une erreur. Le signal SIGSEGV a été envoyé : *Segmentation Fault*.") + elif p.returncode == 252: + montest_output = rst.get_admonition("warning", "**Erreur d'exécution**", "Votre code a tenté d'allouer plus de mémoire que disponible.") + feedback.set_tag("memory", True) + elif p.returncode == 253: + montest_output = rst.get_admonition("warning", "**Erreur d'exécution**", "Votre code a pris trop de temps pour s'exécuter.") + else: + montest_output = rst.get_admonition("warning", "**Erreur d'exécution**", "Votre code a produit une erreur.") + feedback.set_global_feedback(rst.indent_block(2, montest_output, " "), True) + exit(0) +#elif run_output: +# feedback.set_global_feedback("- Sortie de votre méthode de test:\n" + rst.indent_block(2, rst.get_codeblock('', run_output), " "), True) + +# Comment to run the tests +#feedback.set_global_feedback("- **Cette note n'est pas finale.** Une série de tests sera exécutée sur votre code après l'examen.\n", True) +#exit(0) + +# Fetch CUnit test results +results_raw = [r.split('#') for r in open('results.txt').read().splitlines()] +results = [{'pid':r[0], 'code':r[1], 'desc':r[2], 'weight':int(r[3]), 'tags': r[4].split(","), 'info_msgs':r[5:]} for r in results_raw] + + +# Produce feedback +if all([r['code'] == 'SUCCESS' for r in results]): + feedback.set_global_feedback("\n- Votre code a passé tous les tests.", True) +else: + feedback.set_global_feedback("\n- Il y a des erreurs dans votre solution.", True) + +score = 0 +total = 0 +tests_result = {} + +for test in results: + total += test['weight'] + for tag in test['tags']: + if tag != "": + feedback.set_tag(tag, True) + if test['code'] == 'SUCCESS': + score += test['weight'] + feedback.set_problem_feedback("* {desc}\n\n => réussi ({weight}/{weight}) pts)\n\n".format(**test)+(" Info: {}\n\n".format(" — ".join(test['info_msgs'])) if test['info_msgs'] else '\n'), + test['pid'], True) + tests_result[test['pid']] = True if tests_result.get(test['pid'], True) else False + else: + feedback.set_problem_feedback("* {desc}\n\n => échoué (0/{weight}) pts)\n\n".format(**test)+(" Info: {}\n\n".format(" — ".join(test['info_msgs'])) if test['info_msgs'] else '\n'), + test['pid'], True) + tests_result[test['pid']] = False + +for pid, result in tests_result.items(): + if result: + feedback.set_problem_result("success", pid) + else: + feedback.set_problem_result("failed", pid) + +with open("../task.yaml", 'r') as stream: + problems = yaml.load(stream)['problems'] + + for name, meta in problems.items(): + if meta['type'] == 'match': + answer = input.get_input(name) + if answer == meta['answer']: + feedback.set_problem_result("success", name) + feedback.set_problem_feedback("Votre réponse est correcte. (1/1 pts)", name, True) + score += 1 + else: + feedback.set_problem_result("failed", name) + feedback.set_problem_feedback("Votre réponse est incorrecte. (0/1 pts)", name, True) + + total += 1 + +score = 100*score/(total if not total == 0 else 1) +feedback.set_grade(score) +feedback.set_global_result("success" if score >= 50 else "failed") diff --git a/tests/data/tasks/strcpy/solutions/sol.c b/tests/data/tasks/strcpy/solutions/sol.c new file mode 100644 index 0000000..c86c537 --- /dev/null +++ b/tests/data/tasks/strcpy/solutions/sol.c @@ -0,0 +1,13 @@ +#include <stdlib.h> + +char *buf_strcpy(const char *src){ + int len = strlen(src) + 1, i; + char * ret = (char*) malloc(sizeof(char)*len); + if (!ret) + return NULL; + + for(i = 0; i < len; i++) + *(ret+i) = *(src+i); + + return ret; +} diff --git a/tests/data/tasks/strcpy/student/CTester/CTester.c b/tests/data/tasks/strcpy/student/CTester/CTester.c new file mode 100644 index 0000000..f4a85fa --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/CTester.c @@ -0,0 +1,353 @@ +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <setjmp.h> +#include <signal.h> +#include <errno.h> +#include <sys/time.h> + +#include <CUnit/CUnit.h> +#include <CUnit/Basic.h> +#include <CUnit/Automated.h> + +#include <libintl.h> +#include <locale.h> +#define _(STRING) gettext(STRING) +#include <dlfcn.h> +#include <malloc.h> + +#include "wrap.h" + +#define TAGS_NB_MAX 20 +#define TAGS_LEN_MAX 30 + +extern bool wrap_monitoring; +extern struct wrap_stats_t stats; +extern struct wrap_monitor_t monitored; +extern struct wrap_fail_t failures; +extern struct wrap_log_t logs; + +extern sigjmp_buf segv_jmp; + +int true_stderr; +int true_stdout; +int pipe_stderr[2], usr_pipe_stderr[2]; +int pipe_stdout[2], usr_pipe_stdout[2]; +extern int stdout_cpy, stderr_cpy; +struct itimerval it_val; + +CU_pSuite pSuite = NULL; + + +struct info_msg { + char *msg; + struct info_msg *next; +}; + +struct __test_metadata { + struct info_msg *fifo_in; + struct info_msg *fifo_out; + char problem[140]; + char descr[250]; + unsigned int weight; + unsigned char nb_tags; + char tags[TAGS_NB_MAX][TAGS_LEN_MAX]; + int err; +} test_metadata; + + +void set_test_metadata(char *problem, char *descr, unsigned int weight) +{ + test_metadata.weight = weight; + strncpy(test_metadata.problem, problem, sizeof(test_metadata.problem)); + strncpy(test_metadata.descr, descr, sizeof(test_metadata.descr)); +} + +void push_info_msg(char *msg) +{ + if (strstr(msg, "#") != NULL || strstr(msg, "\n") != NULL) { + test_metadata.err = EINVAL; + return; + } + + struct info_msg *item = malloc(sizeof(struct info_msg)); + if (item == NULL) + test_metadata.err = ENOMEM; + + item->next = NULL; + item->msg = malloc(strlen(msg) + 1); + if (item->msg == NULL) + test_metadata.err = ENOMEM; + + strcpy(item->msg, msg); + if (test_metadata.fifo_in == NULL && test_metadata.fifo_out == NULL) { + test_metadata.fifo_in = item; + test_metadata.fifo_out = item; + } else { + test_metadata.fifo_out->next = item; + test_metadata.fifo_out = item; + } +} + +void set_tag(char *tag) +{ + int i=0; + while (tag[i] != '\0' && i < TAGS_LEN_MAX) { + if (!isalnum(tag[i]) && tag[i] != '-' && tag[i] != '_') + return; + i++; + } + + if (test_metadata.nb_tags < TAGS_NB_MAX) + strncpy(test_metadata.tags[test_metadata.nb_tags++], tag, TAGS_LEN_MAX); +} + +void segv_handler(int sig, siginfo_t *unused, void *unused2) { + wrap_monitoring = false; + push_info_msg(_("Your code produced a segfault.")); + set_tag("sigsegv"); + wrap_monitoring = true; + siglongjmp(segv_jmp, 1); +} + +void alarm_handler(int sig, siginfo_t *unused, void *unused2) +{ + wrap_monitoring = false; + push_info_msg(_("Your code exceeded the maximal allowed execution time.")); + set_tag("timeout"); + wrap_monitoring = true; + siglongjmp(segv_jmp, 1); +} + + +int sandbox_begin() +{ + // Start timer + it_val.it_value.tv_sec = 2; + it_val.it_value.tv_usec = 0; + it_val.it_interval.tv_sec = 0; + it_val.it_interval.tv_usec = 0; + setitimer(ITIMER_REAL, &it_val, NULL); + + // Intercepting stdout and stderr + dup2(pipe_stdout[1], STDOUT_FILENO); + dup2(pipe_stderr[1], STDERR_FILENO); + // Emptying the user pipes + char buf[BUFSIZ]; + int n; + while ((n = read(usr_pipe_stdout[0], buf, BUFSIZ)) > 0); + while ((n = read(usr_pipe_stderr[0], buf, BUFSIZ)) > 0); + + wrap_monitoring = true; + return 0; +} + +void sandbox_fail() +{ + CU_FAIL("Segfault or timeout"); +} + +void sandbox_end() +{ + wrap_monitoring = false; + + // Remapping stderr to the orignal one ... + dup2(true_stdout, STDOUT_FILENO); // TODO + dup2(true_stderr, STDERR_FILENO); + + // ... and looking for a double free warning + char buf[BUFSIZ]; + int n; + while ((n = read(pipe_stdout[0], buf, BUFSIZ)) > 0) { + write(usr_pipe_stdout[1], buf, n); + write(STDOUT_FILENO, buf, n); + } + + + while ((n = read(pipe_stderr[0], buf, BUFSIZ)) > 0) { + if (strstr(buf, "double free or corruption") != NULL) { + CU_FAIL("Double free or corruption"); + push_info_msg(_("Your code produced a double free.")); + set_tag("double_free"); + } + write(usr_pipe_stderr[1], buf, n); + write(STDERR_FILENO, buf, n); + } + + + it_val.it_value.tv_sec = 0; + it_val.it_value.tv_usec = 0; + it_val.it_interval.tv_sec = 0; + it_val.it_interval.tv_usec = 0; + setitimer(ITIMER_REAL, &it_val, NULL); +} + + +int init_suite1(void) +{ + return 0; +} + +int clean_suite1(void) +{ + return 0; +} + +void start_test() +{ + bzero(&test_metadata,sizeof(test_metadata)); + bzero(&stats,sizeof(stats)); + bzero(&failures,sizeof(failures)); + bzero(&monitored,sizeof(monitored)); + bzero(&logs,sizeof(logs)); +} + +int __real_exit(int status); +int __wrap_exit(int status){ + return status; +} + +int run_tests(int argc, char *argv[], void *tests[], int nb_tests) { + for (int i=1; i < argc; i++) { + if (!strncmp(argv[i], "LANGUAGE=", 9)) + putenv(argv[i]); + } + setlocale (LC_ALL, ""); + bindtextdomain("tests", getenv("PWD")); + bind_textdomain_codeset("messages", "UTF-8"); + textdomain("tests"); + + mallopt(M_PERTURB, 142); // newly allocated memory with malloc will be set to ~142 + + // Code for detecting properly double free errors + mallopt(M_CHECK_ACTION, 1); // don't abort if double free + true_stderr = dup(STDERR_FILENO); // preparing a non-blocking pipe for stderr + true_stdout = dup(STDOUT_FILENO); // preparing a non-blocking pipe for stderr + + int *pipes[] = {pipe_stderr, pipe_stdout, usr_pipe_stdout, usr_pipe_stderr}; + for(int i=0; i < 4; i++) { // Configuring pipes to be non-blocking + pipe(pipes[i]); + int flags = fcntl(pipes[i][0], F_GETFL, 0); + fcntl(pipes[i][0], F_SETFL, flags | O_NONBLOCK); + } + stdout_cpy = usr_pipe_stdout[0]; + stderr_cpy = usr_pipe_stderr[0]; + + putenv("LIBC_FATAL_STDERR_=2"); // needed otherwise libc doesn't print to program's stderr + + /* make sure that we catch segmentation faults */ + struct sigaction sa; + + memset(&sa, 0, sizeof(sigaction)); + sigemptyset(&sa.sa_mask); + static char stack[SIGSTKSZ]; + stack_t ss = { + .ss_size = SIGSTKSZ, + .ss_sp = stack, + }; + + sa.sa_flags = SA_NODEFER|SA_ONSTACK|SA_RESTART; + sa.sa_sigaction = segv_handler; + sigaltstack(&ss, 0); + sigfillset(&sa.sa_mask); + int ret = sigaction(SIGSEGV, &sa, NULL); + if (ret) + return ret; + sa.sa_sigaction = alarm_handler; + ret = sigaction(SIGALRM, &sa, NULL); + if (ret) + return ret; + + + /* Output file containing succeeded / failed tests */ + FILE* f_out = fopen("results.txt", "w"); + if (!f_out) + return -ENOENT; + + + /* initialize the CUnit test registry */ + if (CUE_SUCCESS != CU_initialize_registry()) + return CU_get_error(); + + /* add a suite to the registry */ + pSuite = CU_add_suite("Suite_1", init_suite1, clean_suite1); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + + for (int i=0; i < nb_tests; i++) { + Dl_info DlInfo; + if (dladdr(tests[i], &DlInfo) == 0) + return -EFAULT; + + CU_pTest pTest; + if ((pTest = CU_add_test(pSuite, DlInfo.dli_sname, tests[i])) == NULL) { + CU_cleanup_registry(); + return CU_get_error(); + } + + printf("\n==== Results for test %s : ====\n", DlInfo.dli_sname); + + start_test(); + + if (CU_basic_run_test(pSuite,pTest) != CUE_SUCCESS) + return CU_get_error(); + + if (test_metadata.err) + return test_metadata.err; + + int nb = CU_get_number_of_tests_failed(); + if (nb > 0) + ret = fprintf(f_out, "%s#FAIL#%s#%d#", test_metadata.problem, + test_metadata.descr, test_metadata.weight); + + else + ret = fprintf(f_out, "%s#SUCCESS#%s#%d#", test_metadata.problem, + test_metadata.descr, test_metadata.weight); + if (ret < 0) + return ret; + + for(int i=0; i < test_metadata.nb_tags; i++) { + ret = fprintf(f_out, "%s", test_metadata.tags[i]); + if (ret < 0) + return ret; + + if (i != test_metadata.nb_tags - 1) { + ret = fprintf(f_out, ","); + if (ret < 0) + return ret; + } + } + + + while (test_metadata.fifo_in != NULL) { + struct info_msg *head = test_metadata.fifo_in; + ret = fprintf(f_out, "#%s", head->msg); + + if (head->msg != NULL) + free(head->msg); + test_metadata.fifo_in = head->next; + free(head); + + if (ret < 0) + return ret; + } + + test_metadata.fifo_out = NULL; + ret = fprintf(f_out, "\n"); + if (ret < 0) + return ret; + + } + + fclose(f_out); + + /* Run all tests using the CUnit Basic interface */ + //CU_basic_run_tests(); + //CU_automated_run_tests(); + CU_cleanup_registry(); + return CU_get_error(); +} diff --git a/tests/data/tasks/strcpy/student/CTester/CTester.h b/tests/data/tasks/strcpy/student/CTester/CTester.h new file mode 100644 index 0000000..4448ade --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/CTester.h @@ -0,0 +1,43 @@ +#include <sys/mman.h> +#include <CUnit/CUnit.h> +#include <setjmp.h> + +#include "wrap.h" +#include "trap.h" + +#include <libintl.h> +#include <locale.h> +#define _(STRING) gettext(STRING) + +#define RUN(...) void *ptr_tests[] = {__VA_ARGS__}; return run_tests(argc, argv, ptr_tests, sizeof(ptr_tests)/sizeof(void*)) +#define BAN_FUNCS(...) +#define SANDBOX_BEGIN sandbox_begin(); if(sigsetjmp(segv_jmp,1) == 0) { (void)0 +#define SANDBOX_END } else { \ + sandbox_fail(); \ + } \ + sandbox_end() + + +// Hidden by macros +int run_tests(int argc, char *argv[], void *tests[], int nb_tests); +int sandbox_begin(); +void sandbox_fail(); +void sandbox_end(); + +// To use inside tests +void set_test_metadata(char *problem, char *descr, unsigned int weight); +void push_info_msg(char *msg); +void set_tag(char *tag); + + +// Set to true to enable monitoring features +bool wrap_monitoring = false; + +struct wrap_stats_t stats; +struct wrap_monitor_t monitored; +struct wrap_fail_t failures; +struct wrap_log_t logs; + +int stdout_cpy, stderr_cpy; + +sigjmp_buf segv_jmp; diff --git a/tests/data/tasks/strcpy/student/CTester/trap.c b/tests/data/tasks/strcpy/student/CTester/trap.c new file mode 100644 index 0000000..2b36c07 --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/trap.c @@ -0,0 +1,37 @@ +#include <sys/mman.h> +#include <unistd.h> +#include <string.h> + +#include "trap.h" + +void *trap_buffer(size_t size, int type, int flags, void *data) +{ + int nb_pages = (size / getpagesize()) + 2; + void *ptr = mmap(NULL, getpagesize() * nb_pages, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (ptr == MAP_FAILED) { + return NULL; + } + + void *buf_start; + if (type == TRAP_LEFT) { + buf_start = ptr + getpagesize(); + mprotect(ptr, getpagesize(), PROT_NONE); + } else if (type == TRAP_RIGHT) { + buf_start = ptr + (nb_pages - 1) * getpagesize() - size; + mprotect(ptr + (nb_pages - 1)*getpagesize(), getpagesize(), PROT_NONE); + } else { + return NULL; + } + + if (data != NULL) + memcpy(buf_start, data, size); + + mprotect(buf_start, size, flags); + + return buf_start; +} + +int free_trap(void *ptr, size_t size) +{ + return munmap(ptr, size); +} diff --git a/tests/data/tasks/strcpy/student/CTester/trap.h b/tests/data/tasks/strcpy/student/CTester/trap.h new file mode 100644 index 0000000..fa29af4 --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/trap.h @@ -0,0 +1,8 @@ +enum { + TRAP_LEFT, + TRAP_RIGHT +}; + + +void *trap_buffer(size_t size, int type, int flags, void *data); +int free_trap(void *ptr, size_t size); diff --git a/tests/data/tasks/strcpy/student/CTester/wrap.h b/tests/data/tasks/strcpy/student/CTester/wrap.h new file mode 100644 index 0000000..e62430a --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/wrap.h @@ -0,0 +1,163 @@ +#include <stdbool.h> +#include <stdint.h> +#include <stddef.h> +#include <errno.h> + +// headers for all stats + +#include "wrap_getpid.h" +#include "wrap_file.h" +#include "wrap_malloc.h" +#include "wrap_mutex.h" +#include "wrap_sleep.h" + +// Basic structures for system call wrapper +// verifies whether the system call needs to be monitored. Each +// supported system call must be referenced here + +// flag must be set to true if the system call must be monitored + +struct wrap_monitor_t { + bool getpid; + bool open; + bool creat; + bool close; + bool read; + bool write; + bool stat; + bool fstat; + bool lseek; + bool free; + bool malloc; + bool calloc; + bool realloc; + bool pthread_mutex_lock; + bool pthread_mutex_trylock; + bool pthread_mutex_unlock; + bool pthread_mutex_init; + bool pthread_mutex_destroy; + bool sleep; +}; + +#define MAX_LOG 1000 + +// log for specific system calls +struct malloc_t { + int n; + struct malloc_elem_t log[MAX_LOG]; +}; + +struct wrap_log_t { + struct malloc_t malloc; +} ; + + +// failures are defined as a bitmask. If set to zero, the system call +// never fails. Otherwise, the fail information is shifted to the right +// and the next system call will fail if its low order bit is set +// we assume that there are no more than 32 system calls that need +// fail. This should be sufficient for most inginious exercices. +// If not, the idea can be extended to support more bits. + +#define FAIL_ALWAYS 0b11111111111111111111111111111111 +#define FAIL_FIRST 0b00000000000000000000000000000001 +#define FAIL_TWICE 0b00000000000000000000000000000011 +#define FAIL_NEVER 0b00000000000000000000000000000000 +#define FAIL_SECOND 0b00000000000000000000000000000010 +#define FAIL_THIRD 0b00000000000000000000000000000100 + +#define FAIL(v) (((v & 0b00000000000000000000000000000001) == 0b00000000000000000000000000000001) ) +#define NEXT(v) (v==FAIL_ALWAYS ? FAIL_ALWAYS : v >> 1) +struct wrap_fail_t { + // bool getpid - this call cannot fail + uint32_t open; // indicates whether next open will fail + int open_ret; // return value if open fails + int open_errno; // errno value set if open fails + + uint32_t creat; + int creat_ret; + int creat_errno; + + uint32_t close; + int close_ret; + int close_errno; + + uint32_t read; + int read_ret; + int read_errno; + + uint32_t write; + int write_ret; + int write_errno; + + uint32_t stat; + int stat_ret; + int stat_errno; + + uint32_t fstat; + int fstat_ret; + int fstat_errno; + + uint32_t lseek; + int lseek_ret; + int lseek_errno; + + uint32_t malloc; + void *malloc_ret; + + uint32_t calloc; + void *calloc_ret; + + uint32_t realloc; + void *realloc_ret; + + uint32_t free; + + uint32_t pthread_mutex_lock; + int pthread_mutex_lock_ret; + int pthread_mutex_lock_errno; + + uint32_t pthread_mutex_trylock; + int pthread_mutex_trylock_ret; + int pthread_mutex_trylock_errno; + + uint32_t pthread_mutex_unlock; + int pthread_mutex_unlock_ret; + int pthread_mutex_unlock_errno; + + uint32_t pthread_mutex_init; + int pthread_mutex_init_ret; + int pthread_mutex_init_errno; + + uint32_t pthread_mutex_destroy; + int pthread_mutex_destroy_ret; + int pthread_mutex_destroy_errno; + + uint32_t sleep; + unsigned int sleep_ret; + +} ; + + +struct wrap_stats_t { + struct stats_getpid_t getpid; + struct stats_open_t open; + struct stats_creat_t creat; + struct stats_close_t close; + struct stats_read_t read; + struct stats_write_t write; + struct stats_stat_t stat; + struct stats_fstat_t fstat; + struct stats_lseek_t lseek; + struct stats_malloc_t malloc; + struct stats_calloc_t calloc; + struct stats_memory_t memory; + struct stats_free_t free; + struct stats_realloc_t realloc; + struct stats_pthread_mutex_lock_t pthread_mutex_lock; + struct stats_pthread_mutex_trylock_t pthread_mutex_trylock; + struct stats_pthread_mutex_unlock_t pthread_mutex_unlock; + struct stats_pthread_mutex_unlock_t pthread_mutex_init; + struct stats_pthread_mutex_unlock_t pthread_mutex_destroy; + struct stats_sleep_t sleep; +}; diff --git a/tests/data/tasks/strcpy/student/CTester/wrap_file.c b/tests/data/tasks/strcpy/student/CTester/wrap_file.c new file mode 100644 index 0000000..122a817 --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/wrap_file.c @@ -0,0 +1,239 @@ +// wrapper for the file operations, open, read, write + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include "wrap.h" + +int __real_open(const char *pathname, int flags, mode_t mode); +int __real_creat(const char *pathname, mode_t mode); +int __real_close(int fd); +ssize_t __real_read(int fd, void *buf, size_t count); +ssize_t __real_write(int fd, const void *buf, size_t count); +int __real_stat(const char* path, struct stat *buf); +int __real_fstat(int fd, struct stat *buf); +int __real_lstat(const char* path, struct stat *buf); +ssize_t __real_pread(int fd, void *buf, size_t count, off_t offset); +ssize_t __real_pwrite(int fd, const void *buf, size_t count, off_t offset); +off_t __real_lseek(int fd, off_t offset, int whence); + +extern bool wrap_monitoring; +extern struct wrap_stats_t stats; +extern struct wrap_monitor_t monitored; +extern struct wrap_fail_t failures; +extern struct wrap_log_t logs; + +int __wrap_open(char *pathname, int flags, mode_t mode) { + + if(!wrap_monitoring || !monitored.open) { + return __real_open(pathname,flags,mode); + } + stats.open.called++; + stats.open.last_params.pathname=pathname; + stats.open.last_params.flags=flags; + stats.open.last_params.mode=mode; + + if (FAIL(failures.open)) { + failures.open=NEXT(failures.open); + errno=failures.open_errno; + stats.open.last_return=failures.open_ret; + return failures.open_ret; + } + failures.open=NEXT(failures.open); + // did not fail + int ret=__real_open(pathname, flags, mode); + stats.open.last_return=ret; + return ret; + +} + +int __wrap_creat(char *pathname, mode_t mode) { + + + if(!wrap_monitoring || !monitored.creat) { + return __real_creat(pathname,mode); + } + stats.creat.called++; + stats.creat.last_params.pathname=pathname; + stats.creat.last_params.mode=mode; + + if (FAIL(failures.creat)) { + failures.creat=NEXT(failures.creat); + errno=failures.creat_errno; + stats.creat.last_return=failures.creat_ret; + return failures.creat_ret; + } + failures.creat=NEXT(failures.creat); + // did not fail + int ret=__real_creat(pathname, mode); + stats.creat.last_return=ret; + return ret; + +} + +int __wrap_close(int fd){ + + if(!wrap_monitoring || !monitored.close) { + return __real_close(fd); + } + stats.close.called++; + stats.close.last_params.fd=fd; + + if (FAIL(failures.close)) { + failures.close=NEXT(failures.close); + errno=failures.close_errno; + stats.close.last_return=failures.close_ret; + return failures.close_ret; + } + failures.close=NEXT(failures.close); + // did not fail + int ret=__real_close(fd); + stats.close.last_return=ret; + return ret; + +} + +int __wrap_read(int fd, void *buf, size_t count){ + + if(!wrap_monitoring || !monitored.read) { + return __real_read(fd,buf,count); + } + stats.read.called++; + stats.read.last_params.fd=fd; + stats.read.last_params.buf=buf; + stats.read.last_params.count=count; + + if (FAIL(failures.read)) { + failures.read=NEXT(failures.read); + errno=failures.read_errno; + stats.read.last_return=failures.read_ret; + return failures.read_ret; + } + failures.read=NEXT(failures.read); + // did not fail + int ret=__real_read(fd,buf,count); + stats.read.last_return=ret; + return ret; + +} + + +int __wrap_write(int fd, void *buf, size_t count){ + + if(!wrap_monitoring || !monitored.write) { + return __real_write(fd,buf,count); + } + stats.write.called++; + stats.write.last_params.fd=fd; + stats.write.last_params.buf=buf; + stats.write.last_params.count=count; + + if (FAIL(failures.write)) { + failures.write=NEXT(failures.write); + errno=failures.write_errno; + stats.write.last_return=failures.write_ret; + return failures.write_ret; + } + failures.write=NEXT(failures.write); + // did not fail + int ret=__real_write(fd,buf,count); + stats.write.last_return=ret; + return ret; + +} + +int __wrap_stat(char *path, struct stat *buf) { + + if(!wrap_monitoring || !monitored.stat) { +return __real_stat(path,buf); + } + stats.stat.called++; + stats.stat.last_params.path=path; + stats.stat.last_params.buf=buf; + + if (FAIL(failures.stat)) { + failures.stat=NEXT(failures.stat); + errno=failures.stat_errno; + stats.stat.last_return=failures.stat_ret; + return failures.stat_ret; + } + failures.stat=NEXT(failures.stat); + // did not fail + int ret=__real_stat(path,buf); + stats.stat.returned_stat.st_dev=buf->st_dev; + stats.stat.returned_stat.st_ino=buf->st_ino; + stats.stat.returned_stat.st_mode=buf->st_mode; + stats.stat.returned_stat.st_nlink=buf->st_nlink; + stats.stat.returned_stat.st_uid=buf->st_uid; + stats.stat.returned_stat.st_gid=buf->st_gid; + stats.stat.returned_stat.st_rdev=buf->st_rdev; + stats.stat.returned_stat.st_size=buf->st_size; + stats.stat.returned_stat.st_blksize=buf->st_blksize; + stats.stat.returned_stat.st_blocks=buf->st_blocks; + stats.stat.returned_stat.st_atime=buf->st_atime; + stats.stat.returned_stat.st_mtime=buf->st_mtime; + stats.stat.returned_stat.st_ctime=buf->st_ctime; + stats.stat.last_return=ret; + return ret; + +} + +int __wrap_fstat(int fd, struct stat *buf) { + + if(!wrap_monitoring || !monitored.fstat) { + return __real_fstat(fd,buf); + } + stats.fstat.called++; + stats.fstat.last_params.fd=fd; + stats.fstat.last_params.buf=buf; + + if (FAIL(failures.fstat)) { + failures.fstat=NEXT(failures.fstat); + errno=failures.fstat_errno; + return failures.fstat_ret; + } + failures.fstat=NEXT(failures.fstat); + // did not fail + int ret=__real_fstat(fd,buf); + stats.fstat.returned_stat.st_dev=buf->st_dev; + stats.fstat.returned_stat.st_ino=buf->st_ino; + stats.fstat.returned_stat.st_mode=buf->st_mode; + stats.fstat.returned_stat.st_nlink=buf->st_nlink; + stats.fstat.returned_stat.st_uid=buf->st_uid; + stats.fstat.returned_stat.st_gid=buf->st_gid; + stats.fstat.returned_stat.st_rdev=buf->st_rdev; + stats.fstat.returned_stat.st_size=buf->st_size; + stats.fstat.returned_stat.st_blksize=buf->st_blksize; + stats.fstat.returned_stat.st_blocks=buf->st_blocks; + stats.fstat.returned_stat.st_atime=buf->st_atime; + stats.fstat.returned_stat.st_mtime=buf->st_mtime; + stats.fstat.returned_stat.st_ctime=buf->st_ctime; + + stats.fstat.last_return=ret; + return ret; + +} + +off_t __wrap_lseek(int fd, off_t offset, int whence) { + + if(!wrap_monitoring || !monitored.lseek) { + return __real_lseek(fd,offset,whence); + } + stats.lseek.called++; + stats.lseek.last_params.fd=fd; + stats.lseek.last_params.offset=offset; + stats.lseek.last_params.whence=whence; + + if (FAIL(failures.lseek)) { + failures.lseek=NEXT(failures.lseek); + errno=failures.lseek_errno; + return failures.lseek_ret; + } + failures.lseek=NEXT(failures.lseek); + // did not fail + off_t ret=__real_lseek(fd,offset,whence); + stats.lseek.last_return=ret; + return ret; +} diff --git a/tests/data/tasks/strcpy/student/CTester/wrap_file.h b/tests/data/tasks/strcpy/student/CTester/wrap_file.h new file mode 100644 index 0000000..7cae21f --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/wrap_file.h @@ -0,0 +1,120 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +// basic structure to record the parameters of the last open call + + +struct params_open_t { + char *pathname; + int flags; + mode_t mode; +}; + +// basic statistics for the utilisation of the open system call + +struct stats_open_t { + int called; // number of times the open system call has been issued + struct params_open_t last_params; // parameters for the last call issued + int last_return; // return value of the last open call issued +}; + + +struct params_creat_t { + char *pathname; + mode_t mode; +}; + +// basic statistics for the utilisation of the creat system call + +struct stats_creat_t { + int called; // number of times the open system call has been issued + struct params_creat_t last_params; // parameters for the last call issued + int last_return; // return value of the last open call issued +}; + + +struct params_close_t { + int fd; +}; + +// basic statistics for the utilisation of the close system call + +struct stats_close_t { + int called; // number of times the open system call has been issued + struct params_close_t last_params; // parameters for the last call issued + int last_return; // return value of the last open call issued +}; + +struct params_read_t { + int fd; + void *buf; + ssize_t count; +}; + +// basic statistics for the utilisation of the read system call + +struct stats_read_t { + int called; // number of times the read system call has been issued + struct params_read_t last_params; // parameters for the last call issued + int last_return; // return value of the last read call issued +}; + +struct params_write_t { + int fd; + void *buf; + ssize_t count; +}; + +// basic statistics for the utilisation of the write system call + +struct stats_write_t { + int called; // number of times the write system call has been issued + struct params_read_t last_params; // parameters for the last call issued + int last_return; // return value of the last read call issued +}; + +struct params_stat_t { + char *path; + struct stat *buf; +}; + +// basic statistics for the utilisation of the stat system call + +struct stats_stat_t { + int called; // number of times the write system call has been issued + struct params_stat_t last_params; // parameters for the last call issued + int last_return; // return value of the last read call issued + struct stat returned_stat; // last returned stat structure +}; + + +struct params_fstat_t { + int fd; + struct stat *buf; +}; + +// basic statistics for the utilisation of the fstat system call + +struct stats_fstat_t { + int called; // number of times the write system call has been issued + struct params_fstat_t last_params; // parameters for the last call issued + int last_return; // return value of the last read call issued + struct stat returned_stat; // last returned stat structure +}; + + +struct params_lseek_t { + int fd; + off_t offset; + int whence; +}; + +// basic statistics for the utilisation of the fstat system call + +struct stats_lseek_t { + int called; // number of times the lseek system call has been issued + struct params_lseek_t last_params; // parameters for the last call issued + int last_return; // return value of the last lseek call issued +}; diff --git a/tests/data/tasks/strcpy/student/CTester/wrap_getpid.c b/tests/data/tasks/strcpy/student/CTester/wrap_getpid.c new file mode 100644 index 0000000..ff2e525 --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/wrap_getpid.c @@ -0,0 +1,42 @@ +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include "wrap.h" // system call wrapper + +// pid_t getpid(void); + +pid_t __real_getpid(void); + +extern bool wrap_monitoring; +extern struct wrap_stats_t stats; +extern struct wrap_monitor_t monitored; +extern struct wrap_fail_t failures; + + +void init_getpid() { + // nothing to do +} + +void clean_getpid() { + // nothing to do +} + +void resetstats_getpid() { + stats.getpid.called=0; + stats.getpid.last_return=0; +} + +pid_t __wrap_getpid(void) { + if(!wrap_monitoring || !monitored.getpid) { + return __real_getpid(); + } + // being monitored + + stats.getpid.called++; + pid_t ret=__real_getpid(); + stats.getpid.last_return=ret; + return ret; + +} + + diff --git a/tests/data/tasks/strcpy/student/CTester/wrap_getpid.h b/tests/data/tasks/strcpy/student/CTester/wrap_getpid.h new file mode 100644 index 0000000..2c1905c --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/wrap_getpid.h @@ -0,0 +1,16 @@ +// never remove statistics from this structure, they could be +// used by existing exercices. You might add some additional information +// if it can help to validate some exercices +#include <sys/types.h> +#include <unistd.h> + +struct stats_getpid_t { + int called; // number of times the system call has been called + pid_t last_return; // last return value for getpid + +}; + +void init_getpid(); +void clean_getpid(); +void resetstats_getpid(); + diff --git a/tests/data/tasks/strcpy/student/CTester/wrap_malloc.c b/tests/data/tasks/strcpy/student/CTester/wrap_malloc.c new file mode 100644 index 0000000..eb345d1 --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/wrap_malloc.c @@ -0,0 +1,206 @@ +/* + * Wrapper for malloc, free and calloc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include <stdio.h> +#include <stdlib.h> + +#include "wrap.h" + +#include <libintl.h> +#include <locale.h> +#define _(STRING) gettext(STRING) + +#define MSG_SIZE 1000 +char msg[MSG_SIZE]; + + +void * __real_malloc(size_t s); +void * __real_calloc(size_t nmemb, size_t s); +void __real_free(void *); +void * __real_realloc(void *ptr, size_t size); + + +extern bool wrap_monitoring; +extern struct wrap_stats_t stats; +extern struct wrap_monitor_t monitored; +extern struct wrap_fail_t failures; +extern struct wrap_log_t logs; + +// +// keeps only MAX_LOG in memory +// +void log_malloc(void *ptr, size_t size) { + + if(ptr!=NULL && logs.malloc.n < MAX_LOG) { + logs.malloc.log[logs.malloc.n].size=size; + logs.malloc.log[logs.malloc.n].ptr=ptr; + logs.malloc.n++; + } +} + +void update_realloc_block(void *ptr, size_t newsize) { + for(int i=0;i<MAX_LOG;i++) { + if(logs.malloc.log[i].ptr==ptr) { + logs.malloc.log[i].size=newsize; + return; + } + } + return ; +} + +size_t find_size_malloc(void *ptr) { + for(int i=0;i<MAX_LOG;i++) { + if(logs.malloc.log[i].ptr==ptr) + return logs.malloc.log[i].size; + } + return -1; +} + + +void * __wrap_malloc(size_t size) { + if(!wrap_monitoring || !monitored.malloc) { + return __real_malloc(size); + } + stats.malloc.called++; + stats.malloc.last_params.size=size; + if(FAIL(failures.malloc)) { + failures.malloc=NEXT(failures.malloc); + return failures.malloc_ret; + } + stats.memory.used+=size; + failures.malloc=NEXT(failures.malloc); + void *ptr=__real_malloc(size); + stats.malloc.last_return=ptr; + log_malloc(ptr,size); + return ptr; +} + +void * __wrap_realloc(void *ptr, size_t size) { + if(!wrap_monitoring || !monitored.realloc) { + return __real_realloc(ptr, size); + } + stats.realloc.called++; + stats.realloc.last_params.size=size; + if(FAIL(failures.realloc)) { + failures.realloc=NEXT(failures.realloc); + return failures.realloc_ret; + } + failures.realloc=NEXT(failures.realloc); + int old_size=find_size_malloc(ptr); + void *r_ptr=__real_realloc(ptr,size); + stats.realloc.last_return=r_ptr; + if(ptr!=NULL) { + stats.memory.used+=size-old_size; + update_realloc_block(ptr,size); + } + return r_ptr; +} + + +void * __wrap_calloc(size_t nmemb, size_t size) { + if(!wrap_monitoring || !monitored.calloc) { + return __real_calloc(nmemb, size); + } + stats.calloc.called++; + stats.calloc.last_params.size=size; + stats.calloc.last_params.nmemb=nmemb; + + if(FAIL(failures.calloc)) { + failures.calloc=NEXT(failures.calloc); + return failures.calloc_ret; + } + stats.memory.used+=nmemb*size; + failures.calloc=NEXT(failures.calloc); + + void *ptr=__real_calloc(nmemb,size); + stats.calloc.last_return=ptr; + log_malloc(ptr,nmemb*size); + return ptr; +} + +int malloc_free_ptr(void *ptr) { + + for(int i=0;i<MAX_LOG;i++) { + if(logs.malloc.log[i].ptr==ptr) { + int size=logs.malloc.log[i].size; + logs.malloc.log[i].size=-1; + logs.malloc.log[i].ptr=NULL; + return size; + } + } + return 0; +} + +void __wrap_free(void *ptr) { + if(!wrap_monitoring || !monitored.free) { + return __real_free(ptr); + } + stats.free.called++; + stats.free.last_params.ptr=ptr; + if(ptr!=NULL) { + stats.memory.used-=malloc_free_ptr(ptr); + + if (FAIL(failures.free)) + failures.free=NEXT(failures.free); + else + __real_free(ptr); + } +} + + +/* +void * find_ptr_malloc(size_t size){ + for(int i=0;i<MAX_LOG;i++) { + if(logs.malloc.log[i].size==size) + return logs.malloc.log[i].ptr; + } + return NULL; +} + +void malloc_log_init(struct malloc_t *l) { + for(int i=0;i<MAX_LOG;i++) { + l->log[i].size=-1; + l->log[i].ptr=NULL; + } + l->n=0; + +} + +*/ +int malloc_allocated() { + int tot=0; + for(int i=0;i<MAX_LOG;i++) { + if(logs.malloc.log[i].ptr!=NULL) { + tot+=(int) logs.malloc.log[i].size; + } + } + return tot; +} + +/* + * returns true if the address has been managed by malloc, false + * otherwise (also false if address has been freed) + */ +int malloced(void *addr) { + for(int i=0;i<logs.malloc.n;i++) { + if(logs.malloc.log[i].ptr<=addr && + (logs.malloc.log[i].ptr+ logs.malloc.log[i].size)>=addr) { + return true; + } + } + return false; + +} diff --git a/tests/data/tasks/strcpy/student/CTester/wrap_malloc.h b/tests/data/tasks/strcpy/student/CTester/wrap_malloc.h new file mode 100644 index 0000000..dcc7a8c --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/wrap_malloc.h @@ -0,0 +1,100 @@ +/* + * Wrapper for malloc, free and calloc + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include <stdio.h> +#include <stdlib.h> +// log for malloc operations + + +struct malloc_elem_t { + size_t size; + void *ptr; +}; + + +// basic structure to record the parameters of the last malloc call + +struct params_malloc_t { + size_t size; +}; + +// basic statistics for the utilisation of the malloc call + +struct stats_malloc_t { + int called; // number of times the malloc call has been issued + struct params_malloc_t last_params; // parameters for the last call issued + void *last_return; // return value of the last malloc call issued +}; + +struct stats_memory_t { + int used; // Total number of bytes allocated +}; + +// basic structure to record the parameters of the last malloc call + +struct params_calloc_t { + size_t nmemb; + size_t size; +}; + +// basic statistics for the utilisation of the calloc call + +struct stats_calloc_t { + int called; // number of times the malloc call has been issued + struct params_calloc_t last_params; // parameters for the last call issued + void *last_return; // return value of the last malloc call issued +}; + + +// basic structure to record the parameters of the last free call + +struct params_free_t { + void *ptr; +}; + +// basic statistics for the utilisation of the free call + +struct stats_free_t { + int called; // number of times the free call has been issued + struct params_free_t last_params; // parameters for the last call issued +}; + + +// basic structure to record the parameters of the last realloc call + +struct params_realloc_t { + void *ptr; + size_t size; +}; + +// basic statistics for the utilisation of the realloc call + +struct stats_realloc_t { + int called; // number of times the malloc call has been issued + struct params_realloc_t last_params; // parameters for the last call issued + void *last_return; // return value of the last realloc call issued +}; + + +// function prototypes + +//void malloc_log_init(struct malloc_t *l); +void malloc_log(void *ptr, size_t size); + +// true if memory was allocated by malloc, false otherwise +int malloced(void *addr); +// total amount of memory allocated by malloc +int malloc_allocated(); \ No newline at end of file diff --git a/tests/data/tasks/strcpy/student/CTester/wrap_mutex.c b/tests/data/tasks/strcpy/student/CTester/wrap_mutex.c new file mode 100644 index 0000000..7b19c17 --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/wrap_mutex.c @@ -0,0 +1,117 @@ +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include "wrap.h" // system call wrapper +#include <pthread.h> + + +//int pthread_mutex_lock(pthread_mutex_t *mutex); +//int pthread_mutex_trylock(pthread_mutex_t *mutex); +//int pthread_mutex_unlock(pthread_mutex_t *mutex); + + +int __real_pthread_mutex_lock(pthread_mutex_t *mutex); +int __real_pthread_mutex_trylock(pthread_mutex_t *mutex); +int __real_pthread_mutex_unlock(pthread_mutex_t *mutex); +int __real_pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); +int __real_pthread_mutex_destroy(pthread_mutex_t *mutex); + +extern bool wrap_monitoring; +extern struct wrap_stats_t stats; +extern struct wrap_monitor_t monitored; +extern struct wrap_fail_t failures; + + +void init_mutex() { + // nothing to do +} + +void clean_mutex() { + // nothing to do +} + +void resetstats_mutex() { + + stats.pthread_mutex_lock.called=0; + stats.pthread_mutex_lock.last_return=0; + stats.pthread_mutex_trylock.called=0; + stats.pthread_mutex_trylock.last_return=0; + stats.pthread_mutex_unlock.called=0; + stats.pthread_mutex_unlock.last_return=0; + stats.pthread_mutex_init.called=0; + stats.pthread_mutex_init.last_return=0; + stats.pthread_mutex_destroy.called=0; + stats.pthread_mutex_destroy.last_return=0; +} + +int __wrap_pthread_mutex_destroy(pthread_mutex_t *mutex) { + if(!wrap_monitoring || !monitored.pthread_mutex_destroy) { + return __real_pthread_mutex_destroy(mutex); + } + // being monitored + + stats.pthread_mutex_destroy.called++; + int ret=__real_pthread_mutex_destroy(mutex); + stats.pthread_mutex_destroy.last_arg=mutex; + stats.pthread_mutex_destroy.last_return=ret; + return ret; +} + + +int __wrap_pthread_mutex_init(pthread_mutex_t *restrict mutex, + const pthread_mutexattr_t *restrict attr) { + if(!wrap_monitoring || !monitored.pthread_mutex_init) { + return __real_pthread_mutex_init(mutex,attr); + } + // being monitored + + stats.pthread_mutex_init.called++; + int ret=__real_pthread_mutex_init(mutex,attr); + stats.pthread_mutex_init.last_arg=mutex; + stats.pthread_mutex_init.last_return=ret; + return ret; + +} + + +pid_t __wrap_pthread_mutex_lock(pthread_mutex_t *mutex) { + if(!wrap_monitoring || !monitored.pthread_mutex_lock) { + return __real_pthread_mutex_lock(mutex); + } + // being monitored + + stats.pthread_mutex_lock.called++; + int ret=__real_pthread_mutex_lock(mutex); + stats.pthread_mutex_lock.last_arg=mutex; + stats.pthread_mutex_lock.last_return=ret; + return ret; + +} + +pid_t __wrap_pthread_mutex_trylock(pthread_mutex_t *mutex) { + if(!wrap_monitoring || !monitored.pthread_mutex_trylock) { + return __real_pthread_mutex_trylock(mutex); + } + // being monitored + + stats.pthread_mutex_trylock.called++; + int ret=__real_pthread_mutex_trylock(mutex); + stats.pthread_mutex_trylock.last_arg=mutex; + stats.pthread_mutex_trylock.last_return=ret; + return ret; + +} + +pid_t __wrap_pthread_mutex_unlock(pthread_mutex_t *mutex) { + if(!wrap_monitoring || !monitored.pthread_mutex_unlock) { + return __real_pthread_mutex_unlock(mutex); + } + // being monitored + + stats.pthread_mutex_unlock.called++; + int ret=__real_pthread_mutex_unlock(mutex); + stats.pthread_mutex_unlock.last_arg=mutex; + stats.pthread_mutex_unlock.last_return=ret; + return ret; + +} diff --git a/tests/data/tasks/strcpy/student/CTester/wrap_mutex.h b/tests/data/tasks/strcpy/student/CTester/wrap_mutex.h new file mode 100644 index 0000000..9a1e5eb --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/wrap_mutex.h @@ -0,0 +1,62 @@ +// never remove statistics from this structure, they could be +// used by existing exercices. You might add some additional information +// if it can help to validate some exercices +#include <sys/types.h> +#include <unistd.h> +#include <pthread.h> + +struct stats_pthread_mutex_lock_t { + int called; // number of times the system call has been called + pid_t last_return; // last return value + pthread_mutex_t *last_arg; // last mutex passed as argument + +}; + +void init_pthread_mutex_lock(); +void clean_pthread_mutex_lock(); +void resetstats_pthread_mutex_lock(); + +struct stats_pthread_mutex_trylock_t { + int called; // number of times the system call has been called + pid_t last_return; // last return value + pthread_mutex_t *last_arg; // last mutex passed as argument +}; + +void init_pthread_mutex_trylock(); +void clean_pthread_mutex_trylock(); +void resetstats_pthread_mutex_trylock(); + + +struct stats_pthread_mutex_unlock_t { + int called; // number of times the system call has been called + pid_t last_return; // last return value + pthread_mutex_t *last_arg; // last mutex passed as argument + +}; + +void init_pthread_mutex_unlock(); +void clean_pthread_mutex_unlock(); +void resetstats_pthread_mutex_unlock(); + +struct stats_pthread_mutex_init_t { + int called; // number of times the system call has been called + pid_t last_return; // last return value + pthread_mutex_t *last_arg; // last mutex passed as argument + +}; + +void init_pthread_mutex_init(); +void clean_pthread_mutex_init(); +void resetstats_pthread_mutex_init(); + +struct stats_pthread_mutex_destroy_t { + int called; // number of times the system call has been called + pid_t last_return; // last return value + pthread_mutex_t *last_arg; // last mutex passed as argument + +}; + +void init_pthread_mutex_destroy(); +void clean_pthread_mutex_destroy(); +void resetstats_pthread_mutex_destroy(); + diff --git a/tests/data/tasks/strcpy/student/CTester/wrap_sleep.c b/tests/data/tasks/strcpy/student/CTester/wrap_sleep.c new file mode 100644 index 0000000..b74a2a6 --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/wrap_sleep.c @@ -0,0 +1,51 @@ +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> +#include "wrap.h" // system call wrapper + +// unsigned int sleep(unsigned int seconds); + +unsigned int __real_sleep(unsigned int); + +extern bool wrap_monitoring; +extern struct wrap_stats_t stats; +extern struct wrap_monitor_t monitored; +extern struct wrap_fail_t failures; + + +void init_sleep() { + // nothing to do +} + +void clean_sleep() { + // nothing to do +} + +void resetstats_sleep() { + stats.sleep.called=0; + stats.sleep.last_return=0; + stats.sleep.last_arg=0; +} + +unsigned int __wrap_sleep(unsigned int time) { + if(!wrap_monitoring || !monitored.sleep) { + return __real_sleep(time); + } + + stats.sleep.called++; + stats.sleep.last_arg = time; + // being monitored + if (FAIL(failures.sleep)) { + failures.sleep=NEXT(failures.sleep); + stats.sleep.last_return=failures.sleep_ret; + return failures.sleep_ret; + } + failures.sleep=NEXT(failures.sleep); + // did not fail + + unsigned int ret=__real_sleep(time); + stats.sleep.last_return=ret; + return ret; +} + + diff --git a/tests/data/tasks/strcpy/student/CTester/wrap_sleep.h b/tests/data/tasks/strcpy/student/CTester/wrap_sleep.h new file mode 100644 index 0000000..6d0a488 --- /dev/null +++ b/tests/data/tasks/strcpy/student/CTester/wrap_sleep.h @@ -0,0 +1,16 @@ +// never remove statistics from this structure, they could be +// used by existing exercices. You might add some additional information +// if it can help to validate some exercices +#include <sys/types.h> +#include <unistd.h> + +struct stats_sleep_t { + int called; // number of times the system call has been called + unsigned int last_return; // last return value for sleep + unsigned int last_arg; // last return value for sleep +}; + +void init_sleep(); +void clean_sleep(); +void resetstats_sleep(); + diff --git a/tests/data/tasks/strcpy/student/Makefile b/tests/data/tasks/strcpy/student/Makefile new file mode 100644 index 0000000..52bdded --- /dev/null +++ b/tests/data/tasks/strcpy/student/Makefile @@ -0,0 +1,41 @@ +# MakeFile for the tasks using Chestier +CC=gcc +CPP=cppcheck +EXEC=tests +CPPFLAGS=--error-exitcode=1 +LDFLAGS=-lcunit -lm -lpthread -ldl -rdynamic +SRC=$(wildcard *.c) CTester/wrap_mutex.c CTester/wrap_malloc.c CTester/wrap_file.c CTester/wrap_sleep.c CTester/CTester.c CTester/trap.c +OBJ=$(SRC:.c=.o) +CFLAGS=-Wall -Werror -DC99 -std=gnu99 -ICTester +WRAP=-Wl,-wrap=pthread_mutex_lock -Wl,-wrap=pthread_mutex_unlock -Wl,-wrap=pthread_mutex_trylock -Wl,-wrap=pthread_mutex_init -Wl,-wrap=pthread_mutex_destroy -Wl,-wrap=malloc -Wl,-wrap=free -Wl,-wrap=realloc -Wl,-wrap=calloc -Wl,-wrap=open -Wl,-wrap=creat -Wl,-wrap=close -Wl,-wrap=read -Wl,-wrap=write -Wl,-wrap=stat -Wl,-wrap=fstat -Wl,-wrap=lseek -Wl,-wrap=exit -Wl,-wrap=sleep + +all: $(EXEC) + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +$(EXEC): $(OBJ) + $(CC) $(WRAP) -o $@ $(OBJ) $(LDFLAGS) + +create-po: + mkdir -p po/fr/ + xgettext --keyword=_ --language=C --add-comments --sort-output --from-code=UTF-8 -o po/tests.pot $(SRC) + msginit --input=po/tests.pot --locale=fr_BE.utf8 --output=po/fr/tests.po + +update-po: + xgettext --keyword=_ --language=C --add-comments --sort-output --from-code=UTF-8 -o po/tests.pot $(SRC) + msgmerge --update po/fr/tests.po po/tests.pot + +compile-mo: + msgfmt --no-hash --output-file=po/fr/tests.mo po/fr/tests.po + mkdir -p fr/LC_MESSAGES/ + cp po/fr/tests.mo fr/LC_MESSAGES/tests.mo + +check: $(SRC) + $(CPP) $(CPPFLAGS) $< + +clean: + rm -f $(EXEC) $(OBJ) + +.PHONY: tests + diff --git a/tests/data/tasks/strcpy/student/fr/LC_MESSAGES/tests.mo b/tests/data/tasks/strcpy/student/fr/LC_MESSAGES/tests.mo new file mode 100644 index 0000000000000000000000000000000000000000..cc8533c9066467d118deb3fe66a0db0e498e7286 GIT binary patch literal 901 zcmaJ<-D=c86plY2<Wjs4Q8@)+l`?CxSZ!0c$ktt0XtmT>&>NXePO?*ynJ_;!`V4yM z3wXB|K7-)Chqu0g*Pd*G?Oy1>$2aH9ch3BMKd)c?;?cN9xJ`(gfX1hmR(ylzp9#(S zSHfe$cftnYH{mYf<|WU&Mf`;LE#a8Z)P1|`dH0Ba5N{Fx>f$?B+WHYOP(CI0Vl);+ z1{$r?5{4i*L+_1pT>?|!P;sF_x^jjZHkw5ZiAoW$P7qU)*f~PUYf<t7cu}bLl*1Ym zXN8hrMTz|jWtCQ`OIDpz<z@v!rtx12gL%eXVY>#IlgX-SDpJ|Aka1D;y(6toF|ll# zvJ=#XHjUum)%3*#!ok+SKgLRF%MML0QZ{nAVX=z9KYAIneT}^7$DVVG5e$NGn*|{o z>_GS^3U)SwFbGJ<j`2)1egCUF;Ks68b7=~0m5u<Omi>CJq(oOg%gg4hi7NN}11@vN zb7V2{GJ=UlnauxYBYKZ_qp^QDJsh9kC+r9QzLFLt?eU?a!&<EE!>Zsy?m{x>+Mpf2 zjwfvUT-Tf>L(RsLK1|3wg6)~G{!!sHFW5xs(nKJuRcMBTU1+Dn`x5pbe6Z`c{xD+Q zA1BHg;Ai-;Ns}Gqasf_)ROL)#YnKi0L|F<}M&Xby7Zo>#boAX8%gR97!+N>sJkq(R mzh1R+im*-ZHK(k#z92d`ORLCK=N8#(Tx{riT-xOV!TSRji4DR4 literal 0 HcmV?d00001 diff --git a/tests/data/tasks/strcpy/student/student_code.c.tpl b/tests/data/tasks/strcpy/student/student_code.c.tpl new file mode 100644 index 0000000..ed4fc2d --- /dev/null +++ b/tests/data/tasks/strcpy/student/student_code.c.tpl @@ -0,0 +1,8 @@ +#include<stdio.h> +#include<stdlib.h> + +#include "student_code.h" + +char *buf_strcpy(const char *src) { + @@strcpy_impl@@ +} diff --git a/tests/data/tasks/strcpy/student/student_code.h b/tests/data/tasks/strcpy/student/student_code.h new file mode 100644 index 0000000..649c060 --- /dev/null +++ b/tests/data/tasks/strcpy/student/student_code.h @@ -0,0 +1,5 @@ +#include<string.h> +#include<stdlib.h> + +char *buf_strcpy(const char *src); + diff --git a/tests/data/tasks/strcpy/student/tests.c b/tests/data/tasks/strcpy/student/tests.c new file mode 100644 index 0000000..1613b60 --- /dev/null +++ b/tests/data/tasks/strcpy/student/tests.c @@ -0,0 +1,94 @@ +/* + * TODO : Check if it works well + */ + +// CTester template + +#include <stdlib.h> +#include "student_code.h" +#include "CTester/CTester.h" + +void test_strcpy_return() { + set_test_metadata("strcpy_impl", _("Check if the string is correctly put in memory"), 1); + + char *ret = NULL; + + const char *stack_src = "Chaine de char de test un peu courte mais pas trop quand meme"; + char *src = (char *)malloc(strlen(stack_src)+1); + if (src == NULL) + CU_FAIL("no mem"); + strcpy(src, stack_src); + + monitored.malloc = true; + monitored.calloc = true; + + SANDBOX_BEGIN; + ret = buf_strcpy(src); + SANDBOX_END; + + + // check if only one call to malloc + int ms = stats.malloc.called; + int cs = stats.calloc.called; + CU_ASSERT_EQUAL(ms, 1); + if (ms > 1){ + push_info_msg(_("You used more than one call to malloc")); + return; + } + + // check if new element is malloced + int mal = malloced((void*) ret); + CU_ASSERT_TRUE(mal); + // if malloced, check the value, else not because it produces buffer overflow due to CUNIT + if (mal && ms) { + if(stats.malloc.last_params.size != strlen(src)+1) { + CU_FAIL("wrong malloc size"); + push_info_msg(_("The allocated memory doesn't the correct size.")); + set_tag("malloc_fail"); + return; + } + if (strncmp(ret, src, strlen(src) + 1) != 0){ + CU_FAIL("wrong string"); + } + free(ret); + } + if(!mal){ + push_info_msg(_("The returned pointer is not malloced")); + set_tag("malloc_fail"); + } + if(cs){ + CU_FAIL(); + set_tag("malloc_fail"); + push_info_msg(_("You should use malloc for this task. Calloc could also work but it's not efficient to use it here since we initialise the memory just after we allocate it")); + } +} + +void test_strcpy_nomem() { + set_test_metadata("strcpy_impl", _("Check the behavior of the function when the call to malloc fails"), 1); + + char *ret = NULL; + char *src = "Chaine de char de test un peu courte mais pas trop quand meme"; + + monitored.malloc = true; + failures.malloc = FAIL_ALWAYS; + failures.malloc_ret = NULL; + + SANDBOX_BEGIN; + ret = buf_strcpy(src); + SANDBOX_END; + + CU_ASSERT_PTR_NULL(ret); + if (ret){ + push_info_msg(_("The return value of your implementation is wrong")); + set_tag("malloc_fail"); + } + + free(ret); + +} + +int main(int argc,char** argv) +{ + BAN_FUNCS(memcpy, memccpy); + RUN(test_strcpy_return, test_strcpy_nomem); +} diff --git a/tests/data/tasks/strcpy/task.yaml b/tests/data/tasks/strcpy/task.yaml new file mode 100644 index 0000000..b11b0a6 --- /dev/null +++ b/tests/data/tasks/strcpy/task.yaml @@ -0,0 +1,50 @@ +accessible: true +author: Nicolas Rybowski +context: 'The classic function ``char *strcpy(char *destination, const char *source);`` + `strcpy(3) <https://linux.die.net/man/3/strcpy>`_ needs a destination buffer where + the source string is copied. We ask you to code a function which allocates a buffer + itself, and then performs the copy. + + + The use of copy functions as ``memcpy`` is not allowed. + + + *Hint* : use `malloc(3) <https://sites.uclouvain.be/SystInfo/manpages/man3/malloc.3.html>`_' +environment: cpp +evaluate: best +groups: false +input_random: '0' +limits: {memory: '100', output: '2', time: '30'} +name: '[S3] Improved strcpy ' +network_grading: false +order: 34 +problems: + strcpy_impl: {default: '', header: "Write the body of the function *buf_strcpy*.\n\ + \n.. code-block:: c\n\n /*\n * Creates a buffer that has the same size\ + \ as src, and copies the content of src to this buffer.\n *\n * @src:\ + \ string to be copied\n * @return: return pointer. if src == NULL or in case\ + \ of error, return NULL\n *\n * Remember that strings are terminated with\ + \ '\\0' and that strlen(\"abc\") returns 3 even if 4 bytes are required to store\ + \ this string.\n */\n char *buf_strcpy(const char *src) {\n", language: c, + name: buf_strcpy, type: code} +stored_submissions: 0 +submission_limit: {amount: -1, period: -1} +tags: + '0': {description: Your code exceeds the maximum allowed time., id: timeout, name: Timeout, + type: 1, visible: true} + '1': {description: '', id: sigsegv, name: Segmentation Fault, type: 1, visible: true} + '10': {description: Your code does not compile with cppcheck, id: cppcheck, name: Cppcheck + fails, type: 1, visible: true} + '2': {description: You code does not compile., id: not_compile, name: Not compile, + type: 1, visible: true} + '3': {description: '', id: memory, name: Memory Exceeded, type: 1, visible: true} + '4': {description: '', id: sigfpe, name: Floating Point Exception, type: 1, visible: true} + '5': {description: Your code produced a double free., id: double_free, name: Double + free, type: 1, visible: true} + '6': {description: You use some banned functions., id: banned_funcs, name: Banned + functions, type: 1, visible: true} + '7': {description: You do not manage the case where malloc() fails., id: malloc_fail, + name: Malloc fail, type: 1, visible: true} + '8': {description: '', id: '', name: S3, type: 2, visible: true} + '9': {description: Usage of malloc(), id: '', name: Malloc, type: 2, visible: true} +weight: 1.0 diff --git a/tests/data/tasks/strcpy/test/submission1.test b/tests/data/tasks/strcpy/test/submission1.test new file mode 100644 index 0000000..60029c0 --- /dev/null +++ b/tests/data/tasks/strcpy/test/submission1.test @@ -0,0 +1,23 @@ +_id: 5a8e8660aff4146a5a4cb2bf +archive: 5b9a61461a750b1640772c86 +courseid: LSINF1252 +custom: {} +grade: 100.0 +input: {'@lang': en, '@username': anonymous, strcpy_impl: "char *ret=(char *) malloc(strlen(src)+1);\n\ + if(src==NULL || ret==NULL)\n return NULL;\nfor(int i=0; i<=strlen(src); i++){\n\ + \ ret[i]=src[i];\n}\nreturn ret;"} +problems: + strcpy_impl: [success, "* Check if the string is correctly put in memory\n\n =>\ + \ r\xE9ussi (1/1) pts)\n\n\n* Check the behavior of the function when the call\ + \ to malloc fails\n\n => r\xE9ussi (1/1) pts)\n\n\n"] +response_type: rst +result: success +state: '' +status: done +stderr: '' +stdout: '' +submitted_on: 2018-02-22 09:59:12.418000 +taskid: strcpy +tests: {} +text: "- Votre code compile.\n\n- Votre code a pass\xE9 tous les tests." +username: anonymous diff --git a/tests/test_task_data.py b/tests/test_task_data.py new file mode 100644 index 0000000..bfcfe9d --- /dev/null +++ b/tests/test_task_data.py @@ -0,0 +1,28 @@ +from task_common import TaskData, task_dir_to_TaskData +from pathlib import Path +import unittest +import os +import sys +project_root = os.path.dirname(sys.modules['__main__'].__file__) +test_data_path = os.path.join('tests', 'data') +task_dirs = [ + { + 'root': Path(os.path.join(test_data_path, 'tasks', 'strcpy')), + 'build': Path(os.path.join(test_data_path, 'tasks', 'strcpy', 'student', 'Makefile')), + 'libs': [Path(os.path.join(test_data_path, 'tasks', 'strcpy', 'student', 'CTester'))] + } +] + +class TaskDataTestCase(unittest.TestCase): + + def setUp(self): + pass + + def test_task_dir_to_TaskData(self): + task_dir = task_dirs[0] + task_data = task_dir_to_TaskData(task_dir['root'], task_dir['build'], task_dir['libs']) + self.assertIsNotNone(task_data) + for prop in ['task', 'build_script', 'lib_dirs', 'annex']: + self.assertIsNotNone(task_data.__dict__[prop], msg=f"Property {prop} is None !") + + -- GitLab