diff --git a/src/main/java/org/olat/ims/qti21/QTI21DeliveryOptions.java b/src/main/java/org/olat/ims/qti21/QTI21DeliveryOptions.java
index 149e77bc4b111e82e34b489dcc1068a9dfd48dce..e1abbb4a6efffdc2ca02ca53319f38588e289f66 100644
--- a/src/main/java/org/olat/ims/qti21/QTI21DeliveryOptions.java
+++ b/src/main/java/org/olat/ims/qti21/QTI21DeliveryOptions.java
@@ -29,10 +29,19 @@ package org.olat.ims.qti21;
  */
 public class QTI21DeliveryOptions {
 
+	private Boolean enableCancel;
 	private Boolean enableSuspend;
 	private Boolean displayScoreProgress;
 	private Boolean displayQuestionProgress;
 
+	public Boolean getEnableCancel() {
+		return enableCancel;
+	}
+
+	public void setEnableCancel(Boolean enableCancel) {
+		this.enableCancel = enableCancel;
+	}
+
 	public Boolean getEnableSuspend() {
 		return enableSuspend;
 	}
@@ -56,8 +65,4 @@ public class QTI21DeliveryOptions {
 	public void setDisplayQuestionProgress(Boolean displayQuestionProgress) {
 		this.displayQuestionProgress = displayQuestionProgress;
 	}
-	
-	
-	
-	
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti21/QTI21Service.java b/src/main/java/org/olat/ims/qti21/QTI21Service.java
index e30966eaba85154fcbfd21bfafd6f62d4e2c9a51..f10dcf7a60996d2af69ffc7a4f5a08674d96fa89 100644
--- a/src/main/java/org/olat/ims/qti21/QTI21Service.java
+++ b/src/main/java/org/olat/ims/qti21/QTI21Service.java
@@ -156,6 +156,8 @@ public interface QTI21Service {
 	
 	public AssessmentTestSession finishTestSession(AssessmentTestSession candidateSession, TestSessionState testSessionState, AssessmentResult assessmentResul, Date timestamp);
 	
+	public void cancelTestSession(AssessmentTestSession candidateSession, TestSessionState testSessionState);
+	
 	public CandidateEvent recordCandidateTestEvent(AssessmentTestSession candidateSession, CandidateTestEventType textEventType,
 			TestSessionState testSessionState, NotificationRecorder notificationRecorder);
 
diff --git a/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java b/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java
index 030c7efc41c0ab245ea609d3f74a40ccf0c9c8d7..b2a79190437ed7e8cecb33fbfe6116abd1c1f7e5 100644
--- a/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java
+++ b/src/main/java/org/olat/ims/qti21/manager/AssessmentTestSessionDAO.java
@@ -119,6 +119,28 @@ public class AssessmentTestSessionDAO {
 				.getResultList();
 	}
 	
+	public int deleteTestSession(AssessmentTestSession testSession) {
+		StringBuilder responseSb  = new StringBuilder();
+		responseSb.append("delete from qtiassessmentresponse response where response.assessmentTestSession.key=:sessionKey");
+		int responses = dbInstance.getCurrentEntityManager()
+				.createQuery(responseSb.toString())
+				.setParameter("sessionKey", testSession.getKey())
+				.executeUpdate();
+		
+		StringBuilder itemSb  = new StringBuilder();
+		itemSb.append("delete from qtiassessmentitemsession itemSession where itemSession.assessmentTestSession.key=:sessionKey");
+		int itemSessions = dbInstance.getCurrentEntityManager()
+				.createQuery(itemSb.toString())
+				.setParameter("sessionKey", testSession.getKey())
+				.executeUpdate();
+		
+		String q = "delete from qtiassessmenttestsession session where session.key=:sessionKey";
+		int sessions = dbInstance.getCurrentEntityManager()
+				.createQuery(q)
+				.setParameter("sessionKey", testSession.getKey())
+				.executeUpdate();
+		return itemSessions + sessions + responses;
+	}
 	
 	public int deleteUserTestSessions(RepositoryEntryRef testEntry) {
 		StringBuilder responseSb  = new StringBuilder();
diff --git a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
index ed189fc9e693645a74759fc94305639de40f0060..fb8889bc76d91c983c7af9c25a34c6c048bbadd9 100644
--- a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
+++ b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
@@ -456,6 +456,26 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
         //maybeScheduleLtiOutcomes(candidateSession, assessmentResult);
         return candidateSession;
 	}
+
+	/**
+	 * Cancel delete the test session, related items session and their responses, the
+	 * assessment result file, the test plan file.
+	 * 
+	 */
+	@Override
+	public void cancelTestSession(AssessmentTestSession candidateSession, TestSessionState testSessionState) {
+		final File myStore = storage.getDirectory(candidateSession.getStorage());
+        final File sessionState = new File(myStore, "testSessionState.xml");
+        final File resultFile = getAssessmentResultFile(candidateSession);
+
+		testSessionDao.deleteTestSession(candidateSession);
+		if(sessionState != null && sessionState.exists()) {
+			sessionState.delete();
+		}
+		if(resultFile != null && resultFile.exists()) {
+			resultFile.delete();
+		}
+	}
 	
     private void recordOutcomeVariables(AssessmentTestSession candidateSession, AbstractResult resultNode) {
         for (final ItemVariable itemVariable : resultNode.getItemVariables()) {
diff --git a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
index cb79b3d7cfb491b74ce89e82c72e16b9f0e28c46..60343c8e9fa4b02111ff5f5e18b3e9db74e61288 100644
--- a/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/AssessmentTestDisplayController.java
@@ -42,6 +42,7 @@ import org.olat.core.gui.components.form.flexible.impl.MultipartFileInfos;
 import org.olat.core.gui.components.form.flexible.impl.elements.FormSubmit;
 import org.olat.core.gui.components.htmlheader.jscss.JSAndCSSComponent;
 import org.olat.core.gui.components.link.Link;
+import org.olat.core.gui.components.panel.StackedPanel;
 import org.olat.core.gui.components.progressbar.ProgressBarItem;
 import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.Controller;
@@ -133,8 +134,13 @@ public class AssessmentTestDisplayController extends BasicController implements
 	private final QTI21DeliveryOptions deliveryOptions;
 	
 	private VelocityContainer mainVC;
+	private final StackedPanel mainPanel;
 	private QtiWorksController qtiWorksCtrl;
 	private TestSessionController testSessionController;
+
+	private DialogBoxController advanceTestPartDialog;
+	private DialogBoxController confirmCancelDialog;
+	private DialogBoxController confirmSuspendDialog;
 	
 	private CandidateEvent lastEvent;
 	private Date currentRequestTimestamp;
@@ -154,6 +160,8 @@ public class AssessmentTestDisplayController extends BasicController implements
 	@Autowired
 	private AssessmentService assessmentService;
 	
+	private final boolean allowResume;
+	
 	/**
 	 * 
 	 * @param ureq
@@ -185,7 +193,7 @@ public class AssessmentTestDisplayController extends BasicController implements
 		marks = qtiService.getMarks(getIdentity(), entry, subIdent, testEntry);
 		
 		deliveryOptions = qtiService.getDeliveryOptions(testEntry);
-		boolean allowResume = deliveryOptions.getEnableSuspend() != null
+		allowResume = deliveryOptions.getEnableSuspend() != null
 				&& deliveryOptions.getEnableSuspend().booleanValue();
 
 		AssessmentTestSession lastSession = null;
@@ -214,11 +222,13 @@ public class AssessmentTestDisplayController extends BasicController implements
         	initQtiWorks(ureq);
         }
         
-        putInitialPanel(mainVC);
+        mainPanel = putInitialPanel(mainVC);
 	}
 	
 	private void initQtiWorks(UserRequest ureq) {
-		qtiWorksCtrl = new QtiWorksController(ureq, getWindowControl(), true);
+		boolean allowCancel = deliveryOptions.getEnableCancel() != null
+				&& deliveryOptions.getEnableCancel().booleanValue();
+		qtiWorksCtrl = new QtiWorksController(ureq, getWindowControl(), allowCancel, allowResume);
     	listenTo(qtiWorksCtrl);
     	mainVC.put("qtirun", qtiWorksCtrl.getInitialComponent());
 	}
@@ -260,8 +270,20 @@ public class AssessmentTestDisplayController extends BasicController implements
 				processAdvanceTestPart(ureq);
 			}
 			mainVC.setDirty(true);
+		} else if(confirmCancelDialog == source) {
+			if(DialogBoxUIFactory.isOkEvent(event) || DialogBoxUIFactory.isYesEvent(event)) {
+				doCancel();
+			}
+		}  else if(confirmSuspendDialog == source) {
+			if(DialogBoxUIFactory.isOkEvent(event) || DialogBoxUIFactory.isYesEvent(event)) {
+				doSuspend();
+			}
 		} else if(qtiWorksCtrl == source) {
-			if(event instanceof QTIWorksAssessmentTestEvent) {
+			if(event == Event.CANCELLED_EVENT) {
+				doConfirmCancel(ureq);
+			} else if("suspend".equals(event.getCommand())) {
+				doConfirmSuspend(ureq);
+			} else if(event instanceof QTIWorksAssessmentTestEvent) {
 				processQTIEvent(ureq, (QTIWorksAssessmentTestEvent)event);
 			}
 		}
@@ -271,6 +293,31 @@ public class AssessmentTestDisplayController extends BasicController implements
 	private void doExitTest(UserRequest ureq) {
 		fireEvent(ureq, new QTI21Event(QTI21Event.EXIT));
 	}
+	
+	private void doConfirmSuspend(UserRequest ureq) {
+		String title = translate("suspend.test");
+		String text = translate("confirm.suspend.test");
+		confirmSuspendDialog = activateOkCancelDialog(ureq, title, text, confirmSuspendDialog);
+	}
+	
+	private void doSuspend() {
+		VelocityContainer suspendedVC = createVelocityContainer("suspended");
+		mainPanel.setContent(suspendedVC);
+	}
+	
+	private void doConfirmCancel(UserRequest ureq) {
+		String title = translate("cancel.test");
+		String text = translate("confirm.cancel.test");
+		confirmCancelDialog = activateOkCancelDialog(ureq, title, text, confirmCancelDialog);
+	}
+	
+	private void doCancel() {
+		VelocityContainer cancelledVC = createVelocityContainer("cancelled");
+		mainPanel.setContent(cancelledVC);
+		TestSessionState testSessionState = testSessionController.getTestSessionState();
+		qtiService.cancelTestSession(candidateSession, testSessionState);
+		//delete database object, file submissions...
+	}
 
 	private void processQTIEvent(UserRequest ureq, QTIWorksAssessmentTestEvent qe) {
 		currentRequestTimestamp = ureq.getRequestTimestamp();
@@ -645,8 +692,6 @@ public class AssessmentTestDisplayController extends BasicController implements
         testSessionController.endCurrentTestPart(requestTimestamp);
 	}
 	
-	private DialogBoxController advanceTestPartDialog;
-	
 	private void confirmAdvanceTestPart(UserRequest ureq) {
 		String title = translate("confirm.advance.testpart.title");
 		String text = translate("confirm.advance.testpart.text");
@@ -972,17 +1017,19 @@ public class AssessmentTestDisplayController extends BasicController implements
 	 */
 	private class QtiWorksController extends AbstractQtiWorksController {
 
-		private FormLink endTestPartButton, closeTestButton, cancelTestButton;
+		private FormLink endTestPartButton, closeTestButton, cancelTestButton, suspendTestButton;
 		private AssessmentTestFormItem qtiEl;
 		private AssessmentTreeFormItem qtiTreeEl;
 		private ProgressBarItem scoreProgress, questionProgress;
 		
-		private final boolean allowCancel, displayQuestionProgress, displayScoreProgress;
+		private final boolean allowCancel, allowSuspend;
+		private final boolean displayQuestionProgress, displayScoreProgress;
 		private final QtiWorksStatus qtiWorksStatus = new QtiWorksStatus();
 		
-		public QtiWorksController(UserRequest ureq, WindowControl wControl, boolean allowCancel) {
+		public QtiWorksController(UserRequest ureq, WindowControl wControl, boolean allowCancel, boolean allowSuspend) {
 			super(ureq, wControl, "at_run");
 			this.allowCancel = allowCancel;
+			this.allowSuspend = allowSuspend;
 			displayScoreProgress = deliveryOptions.getDisplayScoreProgress() != null
 					&& deliveryOptions.getDisplayScoreProgress().booleanValue();
 			displayQuestionProgress = deliveryOptions.getDisplayQuestionProgress() != null
@@ -1009,7 +1056,10 @@ public class AssessmentTestDisplayController extends BasicController implements
 			endTestPartButton = uifactory.addFormLink("endTest", endName, null, formLayout, Link.BUTTON);
 			closeTestButton = uifactory.addFormLink("closeTest", "assessment.test.close.test", null, formLayout, Link.BUTTON);
 			if(allowCancel) {
-				cancelTestButton = uifactory.addFormLink("cancelTest", "cancel", null, formLayout, Link.BUTTON);
+				cancelTestButton = uifactory.addFormLink("cancelTest", "cancel.test", null, formLayout, Link.BUTTON);
+			}
+			if(allowSuspend) {
+				suspendTestButton = uifactory.addFormLink("suspendTest", "suspend.test", null, formLayout, Link.BUTTON);
 			}
 
 			ResourceLocator fileResourceLocator = new PathResourceLocator(fUnzippedDirRoot.toPath());
@@ -1068,6 +1118,8 @@ public class AssessmentTestDisplayController extends BasicController implements
 				doCloseTest(ureq);
 			} else if(cancelTestButton == source) {
 				doCancelTest(ureq);
+			} else if(suspendTestButton == source) {
+				doSuspendTest(ureq);
 			} else if(source == qtiEl || source == qtiTreeEl) {
 				if(event instanceof QTIWorksAssessmentTestEvent) {
 					fireEvent(ureq, event);
@@ -1119,7 +1171,11 @@ public class AssessmentTestDisplayController extends BasicController implements
 		}
 		
 		private void doCancelTest(UserRequest ureq) {
-			
+			fireEvent(ureq, Event.CANCELLED_EVENT);
+		}
+		
+		private void doSuspendTest(UserRequest ureq) {
+			fireEvent(ureq, new Event("suspend"));
 		}
 		
 		private void updateGUI() {
diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21OptionsController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21DeliveryOptionsController.java
similarity index 85%
rename from src/main/java/org/olat/ims/qti21/ui/QTI21OptionsController.java
rename to src/main/java/org/olat/ims/qti21/ui/QTI21DeliveryOptionsController.java
index 782ec0217bad3aa7900c790dded3c63244540a47..9acfa96b410a1bbb4e3914b106ab8f1aac030c70 100644
--- a/src/main/java/org/olat/ims/qti21/ui/QTI21OptionsController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/QTI21DeliveryOptionsController.java
@@ -43,12 +43,12 @@ import org.springframework.beans.factory.annotation.Autowired;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class QTI21OptionsController extends FormBasicController implements Activateable2 {
+public class QTI21DeliveryOptionsController extends FormBasicController implements Activateable2 {
 	
 	private static final String[] onKeys = new String[]{ "on" };
 	private static final String[] onValues = new String[]{ "" };
 	
-	private MultipleSelectionElement enableSuspendEl, displayQuestionProgressEl, displayScoreProgressEl;
+	private MultipleSelectionElement enableCancelEl, enableSuspendEl, displayQuestionProgressEl, displayScoreProgressEl;
 	
 	private final RepositoryEntry testEntry;
 	private final QTI21DeliveryOptions deliveryOptions;
@@ -56,7 +56,7 @@ public class QTI21OptionsController extends FormBasicController implements Activ
 	@Autowired
 	private QTI21Service qtiService;
 	
-	public QTI21OptionsController(UserRequest ureq, WindowControl wControl, RepositoryEntry testEntry) {
+	public QTI21DeliveryOptionsController(UserRequest ureq, WindowControl wControl, RepositoryEntry testEntry) {
 		super(ureq, wControl);
 		this.testEntry = testEntry;
 		this.deliveryOptions = qtiService.getDeliveryOptions(testEntry);
@@ -82,6 +82,11 @@ public class QTI21OptionsController extends FormBasicController implements Activ
 			enableSuspendEl.select(onKeys[0], true);
 		}
 		
+		enableCancelEl = uifactory.addCheckboxesHorizontal("cancel", "qti.form.enablecancel", formLayout, onKeys, onValues);
+		if(deliveryOptions.getEnableCancel() != null && deliveryOptions.getEnableCancel().booleanValue()) {
+			enableCancelEl.select(onKeys[0], true);
+		}
+		
 		FormLayoutContainer buttonsLayout = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
 		buttonsLayout.setRootForm(mainForm);
 		formLayout.add(buttonsLayout);
@@ -100,6 +105,12 @@ public class QTI21OptionsController extends FormBasicController implements Activ
 
 	@Override
 	protected void formOK(UserRequest ureq) {
+		if(enableCancelEl.isAtLeastSelected(1)) {
+			deliveryOptions.setEnableCancel(Boolean.TRUE);
+		} else {
+			deliveryOptions.setEnableCancel(Boolean.FALSE);
+		}
+		
 		if(enableSuspendEl.isAtLeastSelected(1)) {
 			deliveryOptions.setEnableSuspend(Boolean.TRUE);
 		} else {
diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21RuntimeController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21RuntimeController.java
index 908d9cc5cd657f5f6e5c637b9353739e42bc4bf2..b2b4733224473c38880d0b0b65c26eeb65b28e51 100644
--- a/src/main/java/org/olat/ims/qti21/ui/QTI21RuntimeController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/QTI21RuntimeController.java
@@ -50,7 +50,7 @@ public class QTI21RuntimeController extends RepositoryEntryRuntimeController  {
 	
 	private Link assessmentLink, testStatisticLink, qtiOptionsLink;
 	
-	private QTI21OptionsController optionsCtrl;
+	private QTI21DeliveryOptionsController optionsCtrl;
 	private AssessmentOverviewController assessmentToolCtrl;
 	private QTI21AssessmentTestStatisticsController statsToolCtr;
 
@@ -123,7 +123,7 @@ public class QTI21RuntimeController extends RepositoryEntryRuntimeController  {
 		WindowControl swControl = addToHistory(ureq, ores, null);
 		
 		if (reSecurity.isEntryAdmin()) {
-			QTI21OptionsController ctrl = new QTI21OptionsController(ureq, swControl, getRepositoryEntry());
+			QTI21DeliveryOptionsController ctrl = new QTI21DeliveryOptionsController(ureq, swControl, getRepositoryEntry());
 			listenTo(ctrl);
 			optionsCtrl = pushController(ureq, "Options", ctrl);
 			currentToolCtr = optionsCtrl;
diff --git a/src/main/java/org/olat/ims/qti21/ui/_content/cancelled.html b/src/main/java/org/olat/ims/qti21/ui/_content/cancelled.html
new file mode 100644
index 0000000000000000000000000000000000000000..76a693d7d0ab6b459b8ffd88f457a3a564b6cc13
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/_content/cancelled.html
@@ -0,0 +1,3 @@
+<div id="o_qti_container">
+	<div class="o_important">$r.translate("assessment.test.cancelled")</div>
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti21/ui/_content/suspended.html b/src/main/java/org/olat/ims/qti21/ui/_content/suspended.html
new file mode 100644
index 0000000000000000000000000000000000000000..c0282a636250948871a78ab6a19528c40ab6e539
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/_content/suspended.html
@@ -0,0 +1,3 @@
+<div id="o_qti_container">
+	<div class="o_important">$r.translate("assessment.test.suspended")</div>
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties
index e4a007b82a11285c5c2e07f8a317b86c9b1718f0..dafc06c53f7045ee5ccd6ca9763d7190a1f55a42 100644
--- a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties
@@ -11,6 +11,8 @@ assessment.item.status.notAnswered=Not answered
 assessment.item.status.modelSolution=Model solution
 assessment.item.modal.feedback=Feedback
 assessment.test.backToTestFeedback=Back to test's feedback
+assessment.test.cancelled=The test has been cancelled.
+assessment.test.suspended=the test has been suspended.
 assessment.test.end.test=Finish test
 assessment.test.end.testPart=Finish test part
 assessment.test.close.test=Close test
@@ -23,14 +25,19 @@ assessment.test.nav.title.multiPartTestMenu=Test part question menu
 assessment.test.nextQuestion=Next Question
 assessment.solution.hide=Hide solution
 assessment.solution.show=Show solution
+cancel.test=$org.olat.modules.iq\:cancelAssess
 command.openassessment=Assessment tool
 command.openteststatistic=Test statistics
+confirm.cancel.test=$org.olat.modules.iq\:confirmCancel
+confirmSuspend=$org.olat.modules.iq\:confirmSuspend
 confirm.advance.testpart.title=Advance test part
 confirm.advance.testpart.text=Do really want to leave this test part and advance further?
+qti.form.enablecancel=$org.olat.course.nodes.iq\:qti.form.enablecancel
 qti.form.enablesuspend=$org.olat.course.nodes.iq\:qti.form.enablesuspend
 qti.form.questionprogress=$org.olat.course.nodes.iq\:qti.form.questionprogress
 qti.form.scoreprogress=$org.olat.course.nodes.iq\:qti.form.scoreprogress
 question.progress.answered=Answered
 question.progress.score=$org.olat.modules.iq\:actualPoints
 question.progress.noMaxScore=$org.olat.modules.iq\:noMaxScore
+suspend.test=$org.olat.modules.iq\:suspendAssess
 tab.options=Options
\ No newline at end of file