diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..dc3b136482a1f385b4023be360543f3d1e874bfc --- /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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/__pycache__/task_common.cpython-38.pyc b/__pycache__/task_common.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad8ee7ac5894589d3e36a4ce2c718dd76fb65eaa Binary files /dev/null and b/__pycache__/task_common.cpython-38.pyc differ diff --git a/run_tests.py b/run_tests.py new file mode 100644 index 0000000000000000000000000000000000000000..788263b68786f680f3c3bbd3f457b23341f0e41b --- /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 49a156fd7b65c65c021f757e68501759081194b3..4859cfcb44663fe1322f4c996b0f1cf71e83fd96 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 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/tests/__pycache__/__init__.cpython-38.pyc b/tests/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f908614706e23bedbec580533cbd7c8575d1e496 Binary files /dev/null and b/tests/__pycache__/__init__.cpython-38.pyc differ 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 Binary files /dev/null and b/tests/__pycache__/test_task_data.cpython-38.pyc differ diff --git a/tests/data/tasks/strcpy/run b/tests/data/tasks/strcpy/run new file mode 100644 index 0000000000000000000000000000000000000000..091a7605cbcb1b0ade49704c19fc9802b8d16010 --- /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 0000000000000000000000000000000000000000..c86c537c1d3e82d7c3e65649095d98a80416e88c --- /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 0000000000000000000000000000000000000000..f4a85fae91e551cd602eb09544575dcb86e95fd6 --- /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 0000000000000000000000000000000000000000..4448aded8abc4f241f189a1a617df2ba28aecd78 --- /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 0000000000000000000000000000000000000000..2b36c07dc2da24dc15521999d650a76bf8f099bc --- /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 0000000000000000000000000000000000000000..fa29af4944997e544703905217c8694b9ae004be --- /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 0000000000000000000000000000000000000000..e62430afad1929c33a361f372cf4d167d1e65569 --- /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 0000000000000000000000000000000000000000..122a817688d7942ca0186e6f513599b764ac8f07 --- /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 0000000000000000000000000000000000000000..7cae21f43b6a51b50b6c8a00db2250b93e61a3b5 --- /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 0000000000000000000000000000000000000000..ff2e52595eb4811da850b6c6a5c975f150c0ddaa --- /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 0000000000000000000000000000000000000000..2c1905c9e304ddbade51f9c06b52653ca63e4b83 --- /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 0000000000000000000000000000000000000000..eb345d19124036d1d897020db3b38b3f21b8da4d --- /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 0000000000000000000000000000000000000000..dcc7a8c6fc83e38ed246fa342820c2003529ef40 --- /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 0000000000000000000000000000000000000000..7b19c1768a114fe970828e73ac4dbc753607154f --- /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 0000000000000000000000000000000000000000..9a1e5eb92cfacd31350e79ba4cc01ff86ed52070 --- /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 0000000000000000000000000000000000000000..b74a2a6c387a729b624c13b85a7f52989224d6b8 --- /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 0000000000000000000000000000000000000000..6d0a488736aac6b1b97c25a3786d23ad959dd54e --- /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 0000000000000000000000000000000000000000..52bdded09c4e4ab1bc2e5ecf7122f3319c16b536 --- /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 Binary files /dev/null and b/tests/data/tasks/strcpy/student/fr/LC_MESSAGES/tests.mo differ 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 0000000000000000000000000000000000000000..ed4fc2d74b159b043438dd41f27ac3779ccff53b --- /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 0000000000000000000000000000000000000000..649c060d753b22176606b0a207aba72c9deca08f --- /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 0000000000000000000000000000000000000000..1613b608eb194bc8663f2f70ded143d3ae195a6b --- /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 0000000000000000000000000000000000000000..b11b0a6b4742ae351313db4c18996313485e36f8 --- /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 0000000000000000000000000000000000000000..60029c0e85832e6871a2f80f7b2bd111bca0d8d6 --- /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 0000000000000000000000000000000000000000..bfcfe9df4ef25dc83cbfb6d521a2320e7e56629c --- /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 !") + +