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