Browse Source

DebugTestContext. TestCase >> debugCase.

Allows for catching (async) error explicitly.
Should be used in #performFailure: (when failed test case is clicked).

This allows to put then assertions in promises' then blocks,
with help of `self async: aBlock`,
and actually see error / failure details in debugger upon click.
Herby Vojčík 4 years ago
parent
commit
06daecf756
3 changed files with 183 additions and 0 deletions
  1. 8 0
      lang/API-CHANGES.txt
  2. 137 0
      lang/src/SUnit.js
  3. 38 0
      lang/src/SUnit.st

+ 8 - 0
lang/API-CHANGES.txt

@@ -1,3 +1,11 @@
+0.25.4:
+
+* Add class DebugTestContext.
+
++ TestCase >>
+  + debugCase
+
+
 0.25.1:
 
 + Collection >>

+ 137 - 0
lang/src/SUnit.js

@@ -400,6 +400,30 @@ return self;
 }; }),
 $globals.TestCase);
 
+$core.addMethod(
+$core.method({
+selector: "debugCase",
+protocol: "running",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "debugCase\x0a\x09\x22Runs a test case in isolated context, debugging all errors.\x22\x0a\x0a\x09(DebugTestContext testCase: self) start",
+referencedClasses: ["DebugTestContext"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["start", "testCase:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv($recv($globals.DebugTestContext)._testCase_(self))._start();
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"debugCase",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.TestCase);
+
 $core.addMethod(
 $core.method({
 selector: "deny:",
@@ -1161,6 +1185,119 @@ return $recv($1)._yourself();
 $globals.TestContext.a$cls);
 
 
+$core.addClass("DebugTestContext", $globals.TestContext, ["finished", "result"], "SUnit");
+//>>excludeStart("ide", pragmas.excludeIdeData);
+$globals.DebugTestContext.comment="I add error debugging to `TestContext`.\x0a\x0aErrors are caught and explicitly passed to `ErrorHandler`.\x0aI am used in `TestCase >> debugCase`.";
+//>>excludeEnd("ide");
+$core.addMethod(
+$core.method({
+selector: "execute:",
+protocol: "running",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aBlock"],
+source: "execute: aBlock\x0a\x09self withErrorDebugging: [ super execute: aBlock ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["withErrorDebugging:", "execute:"]
+}, function ($methodClass){ return function (aBlock){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self._withErrorDebugging_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return [(
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.supercall = true,
+//>>excludeEnd("ctx");
+($methodClass.superclass||$boot.nilAsClass).fn.prototype._execute_.call($self,aBlock))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx2.supercall = false
+//>>excludeEnd("ctx");
+][0];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"execute:",{aBlock:aBlock})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.DebugTestContext);
+
+$core.addMethod(
+$core.method({
+selector: "withErrorDebugging:",
+protocol: "private",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aBlock"],
+source: "withErrorDebugging: aBlock\x0a\x09aBlock\x0a\x09\x09on: Error\x0a\x09\x09do: [ :ex | ErrorHandler handleError: ex ]",
+referencedClasses: ["Error", "ErrorHandler"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["on:do:", "handleError:"]
+}, function ($methodClass){ return function (aBlock){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv(aBlock)._on_do_($globals.Error,(function(ex){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv($globals.ErrorHandler)._handleError_(ex);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({ex:ex},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"withErrorDebugging:",{aBlock:aBlock})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.DebugTestContext);
+
+
+$core.addMethod(
+$core.method({
+selector: "testCase:result:finished:",
+protocol: "instance creation",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aTestCase", "aTestResult", "aBlock"],
+source: "testCase: aTestCase result: aTestResult finished: aBlock\x0a\x09^ (super testCase: aTestCase)\x0a\x09\x09result: aTestResult;\x0a\x09\x09finished: aBlock;\x0a\x09\x09yourself",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["result:", "testCase:", "finished:", "yourself"]
+}, function ($methodClass){ return function (aTestCase,aTestResult,aBlock){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+$1=[(
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = true,
+//>>excludeEnd("ctx");
+($methodClass.superclass||$boot.nilAsClass).fn.prototype._testCase_.call($self,aTestCase))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.supercall = false
+//>>excludeEnd("ctx");
+][0];
+$recv($1)._result_(aTestResult);
+$recv($1)._finished_(aBlock);
+return $recv($1)._yourself();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"testCase:result:finished:",{aTestCase:aTestCase,aTestResult:aTestResult,aBlock:aBlock})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.DebugTestContext.a$cls);
+
+
 $core.addClass("ReportingTestContext", $globals.TestContext, ["finished", "result"], "SUnit");
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.ReportingTestContext.comment="I add `TestResult` reporting to `TestContext`.\x0a\x0aErrors are caught and save into a `TestResult`,\x0aWhen test case is finished (which can be later for async tests), a callback block is executed; this is used by a `TestSuiteRunner`.";

+ 38 - 0
lang/src/SUnit.st

@@ -152,6 +152,12 @@ signalFailure: aString
 
 !TestCase methodsFor: 'running'!
 
+debugCase
+	"Runs a test case in isolated context, debugging all errors."
+
+	(DebugTestContext testCase: self) start
+!
+
 performTest
 	asyncTimeout := nil.
 	self perform: self selector
@@ -305,6 +311,38 @@ testCase: aTestCase
 		yourself
 ! !
 
+TestContext subclass: #DebugTestContext
+	slots: {#finished. #result}
+	package: 'SUnit'!
+!DebugTestContext commentStamp!
+I add error debugging to `TestContext`.
+
+Errors are caught and explicitly passed to `ErrorHandler`.
+I am used in `TestCase >> debugCase`.!
+
+!DebugTestContext methodsFor: 'private'!
+
+withErrorDebugging: aBlock
+	aBlock
+		on: Error
+		do: [ :ex | ErrorHandler handleError: ex ]
+! !
+
+!DebugTestContext methodsFor: 'running'!
+
+execute: aBlock
+	self withErrorDebugging: [ super execute: aBlock ]
+! !
+
+!DebugTestContext class methodsFor: 'instance creation'!
+
+testCase: aTestCase result: aTestResult finished: aBlock
+	^ (super testCase: aTestCase)
+		result: aTestResult;
+		finished: aBlock;
+		yourself
+! !
+
 TestContext subclass: #ReportingTestContext
 	slots: {#finished. #result}
 	package: 'SUnit'!