Browse Source

Fix SUnit running.

Tests that return resolved promise
but fail in tearDown now show the error
upon clicking in the runner.
Herby Vojčík 4 months ago
parent
commit
3f9c32c11e
4 changed files with 98 additions and 22 deletions
  1. 65 6
      lang/src/SUnit-Tests.js
  2. 16 4
      lang/src/SUnit-Tests.st
  3. 13 9
      lang/src/SUnit.js
  4. 4 3
      lang/src/SUnit.st

+ 65 - 6
lang/src/SUnit-Tests.js

@@ -484,6 +484,40 @@ catch(e) {if(e===$early)return e[0]; throw e}
 }; }),
 $globals.SUnitAsyncTest);
 
+$core.addMethod(
+$core.method({
+selector: "fakePassFailingInTearDown",
+protocol: "helpers",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "fakePassFailingInTearDown\x0a\x09flag := 'bad'.\x0a\x09self timeout: 10.\x0a\x09(self async: [ self finished ]) fork",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "fork", "async:", "finished"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self.flag="bad";
+$self._timeout_((10));
+$recv($self._async_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._finished();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+})))._fork();
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"fakePassFailingInTearDown",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.SUnitAsyncTest);
+
 $core.addMethod(
 $core.method({
 selector: "fakeTimeout",
@@ -597,7 +631,7 @@ selector: "testAsyncErrorsAndFailures",
 protocol: "tests",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "testAsyncErrorsAndFailures\x0a\x09| suite runner result assertBlock |\x0a\x09suite := #(fakeError fakeErrorFailingInTearDown fakeFailure testPass) collect: [ :each | self class selector: each ].\x0a\x09runner := TestSuiteRunner on: suite.\x0a\x09self timeout: 200.\x0a\x09result := runner result.\x0a\x09assertBlock := self async: [\x0a\x09\x09self assert: (self selectorSetOf: result errors) equals: #(fakeError) asSet.\x0a\x09\x09self assert: (self selectorSetOf: result failures) equals: #(fakeErrorFailingInTearDown fakeFailure) asSet.\x0a\x09\x09self finished\x0a\x09].\x0a\x09runner announcer on: ResultAnnouncement do: [ :ann |\x0a\x09\x09(ann result == result and: [ result runs = result total ]) ifTrue: assertBlock ].\x0a\x09runner run",
+source: "testAsyncErrorsAndFailures\x0a\x09| suite runner result assertBlock |\x0a\x09suite := #(fakeError fakePassFailingInTearDown fakeErrorFailingInTearDown fakeFailure testPass) collect: [ :each | self class selector: each ].\x0a\x09runner := TestSuiteRunner on: suite.\x0a\x09self timeout: 200.\x0a\x09result := runner result.\x0a\x09assertBlock := self async: [\x0a\x09\x09self assert: (self selectorSetOf: result errors) equals: #(fakeError) asSet.\x0a\x09\x09self assert: (self selectorSetOf: result failures) equals: #(fakePassFailingInTearDown fakeErrorFailingInTearDown fakeFailure) asSet.\x0a\x09\x09self finished\x0a\x09].\x0a\x09runner announcer on: ResultAnnouncement do: [ :ann |\x0a\x09\x09(ann result == result and: [ result runs = result total ]) ifTrue: assertBlock ].\x0a\x09runner run",
 referencedClasses: ["TestSuiteRunner", "ResultAnnouncement"],
 //>>excludeEnd("ide");
 pragmas: [],
@@ -609,7 +643,7 @@ var suite,runner,result,assertBlock;
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
 var $1;
-suite=["fakeError", "fakeErrorFailingInTearDown", "fakeFailure", "testPass"]._collect_((function(each){
+suite=["fakeError", "fakePassFailingInTearDown", "fakeErrorFailingInTearDown", "fakeFailure", "testPass"]._collect_((function(each){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx2) {
 //>>excludeEnd("ctx");
@@ -642,7 +676,7 @@ return $core.withContext(function($ctx2) {
 ,$ctx2.sendIdx["assert:equals:"]=1
 //>>excludeEnd("ctx");
 ][0];
-$self._assert_equals_($self._selectorSetOf_($recv(result)._failures()),["fakeErrorFailingInTearDown", "fakeFailure"]._asSet());
+$self._assert_equals_($self._selectorSetOf_($recv(result)._failures()),["fakePassFailingInTearDown", "fakeErrorFailingInTearDown", "fakeFailure"]._asSet());
 return $self._finished();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)});
@@ -1289,6 +1323,31 @@ catch(e) {if(e===$early)return e[0]; throw e}
 }; }),
 $globals.SUnitPromiseTest);
 
+$core.addMethod(
+$core.method({
+selector: "fakePassFailingInTearDown",
+protocol: "helpers",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "fakePassFailingInTearDown\x0a\x09flag := 'bad'.\x0a\x09self timeout: 10.\x0a\x09^ Promise new",
+referencedClasses: ["Promise"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["timeout:", "new"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$self.flag="bad";
+$self._timeout_((10));
+return $recv($globals.Promise)._new();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"fakePassFailingInTearDown",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.SUnitPromiseTest);
+
 $core.addMethod(
 $core.method({
 selector: "fakePromiseWithoutTimeout",
@@ -1588,7 +1647,7 @@ selector: "testPromiseErrorsAndFailures",
 protocol: "tests",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: [],
-source: "testPromiseErrorsAndFailures\x0a\x09| suite runner result |\x0a\x09suite := #(fakeError fakeErrorFailingInTearDown fakeFailure testPass) collect: [ :each | self class selector: each ].\x0a\x09runner := TestSuiteRunner on: suite.\x0a\x09self timeout: 200.\x0a\x09result := runner result.\x0a\x09^ Promise new: [ :model |\x0a\x09\x09runner announcer on: ResultAnnouncement do: [ :ann |\x0a\x09\x09\x09(ann result == result and: [ result runs = result total ]) ifTrue: [ model do: [\x0a\x09\x09\x09\x09self assert: (self selectorSetOf: result errors) equals: #(fakeError) asSet.\x0a\x09\x09\x09\x09self assert: (self selectorSetOf: result failures) equals: #(fakeErrorFailingInTearDown fakeFailure) asSet ] ] ].\x0a\x09\x09runner run ]",
+source: "testPromiseErrorsAndFailures\x0a\x09| suite runner result |\x0a\x09suite := #(fakeError fakePassFailingInTearDown fakeErrorFailingInTearDown fakeFailure testPass) collect: [ :each | self class selector: each ].\x0a\x09runner := TestSuiteRunner on: suite.\x0a\x09self timeout: 200.\x0a\x09result := runner result.\x0a\x09^ Promise new: [ :model |\x0a\x09\x09runner announcer on: ResultAnnouncement do: [ :ann |\x0a\x09\x09\x09(ann result == result and: [ result runs = result total ]) ifTrue: [ model do: [\x0a\x09\x09\x09\x09self assert: (self selectorSetOf: result errors) equals: #(fakeError) asSet.\x0a\x09\x09\x09\x09self assert: (self selectorSetOf: result failures) equals: #(fakePassFailingInTearDown fakeErrorFailingInTearDown fakeFailure) asSet ] ] ].\x0a\x09\x09runner run ]",
 referencedClasses: ["TestSuiteRunner", "Promise", "ResultAnnouncement"],
 //>>excludeEnd("ide");
 pragmas: [],
@@ -1600,7 +1659,7 @@ var suite,runner,result;
 return $core.withContext(function($ctx1) {
 //>>excludeEnd("ctx");
 var $1;
-suite=["fakeError", "fakeErrorFailingInTearDown", "fakeFailure", "testPass"]._collect_((function(each){
+suite=["fakeError", "fakePassFailingInTearDown", "fakeErrorFailingInTearDown", "fakeFailure", "testPass"]._collect_((function(each){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx2) {
 //>>excludeEnd("ctx");
@@ -1647,7 +1706,7 @@ return $core.withContext(function($ctx4) {
 ,$ctx4.sendIdx["assert:equals:"]=1
 //>>excludeEnd("ctx");
 ][0];
-return $self._assert_equals_($self._selectorSetOf_($recv(result)._failures()),["fakeErrorFailingInTearDown", "fakeFailure"]._asSet());
+return $self._assert_equals_($self._selectorSetOf_($recv(result)._failures()),["fakePassFailingInTearDown", "fakeErrorFailingInTearDown", "fakeFailure"]._asSet());
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx4) {$ctx4.fillBlock({},$ctx3,6)});
 //>>excludeEnd("ctx");

+ 16 - 4
lang/src/SUnit-Tests.st

@@ -99,6 +99,12 @@ fakeNonLifoReturn
 	flag := (self async: [ flag := 'ok'. ^ 'non-lifo' ]) valueWithTimeout: 20
 !
 
+fakePassFailingInTearDown
+	flag := 'bad'.
+	self timeout: 10.
+	(self async: [ self finished ]) fork
+!
+
 fakeTimeout
 	self timeout: 10.
 	(self async: [ self finished ]) valueWithTimeout: 20
@@ -124,13 +130,13 @@ tearDown
 
 testAsyncErrorsAndFailures
 	| suite runner result assertBlock |
-	suite := #(fakeError fakeErrorFailingInTearDown fakeFailure testPass) collect: [ :each | self class selector: each ].
+	suite := #(fakeError fakePassFailingInTearDown fakeErrorFailingInTearDown fakeFailure testPass) collect: [ :each | self class selector: each ].
 	runner := TestSuiteRunner on: suite.
 	self timeout: 200.
 	result := runner result.
 	assertBlock := self async: [
 		self assert: (self selectorSetOf: result errors) equals: #(fakeError) asSet.
-		self assert: (self selectorSetOf: result failures) equals: #(fakeErrorFailingInTearDown fakeFailure) asSet.
+		self assert: (self selectorSetOf: result failures) equals: #(fakePassFailingInTearDown fakeErrorFailingInTearDown fakeFailure) asSet.
 		self finished
 	].
 	runner announcer on: ResultAnnouncement do: [ :ann |
@@ -251,6 +257,12 @@ fakeNonLifoReturn
 	^ flag then: [ flag := 'ok'. ^ 'non-lifo' ]
 !
 
+fakePassFailingInTearDown
+	flag := 'bad'.
+	self timeout: 10.
+	^ Promise new
+!
+
 fakePromiseWithoutTimeout
 	^ Promise delayMilliseconds: 10
 !
@@ -316,7 +328,7 @@ testPass
 
 testPromiseErrorsAndFailures
 	| suite runner result |
-	suite := #(fakeError fakeErrorFailingInTearDown fakeFailure testPass) collect: [ :each | self class selector: each ].
+	suite := #(fakeError fakePassFailingInTearDown fakeErrorFailingInTearDown fakeFailure testPass) collect: [ :each | self class selector: each ].
 	runner := TestSuiteRunner on: suite.
 	self timeout: 200.
 	result := runner result.
@@ -324,7 +336,7 @@ testPromiseErrorsAndFailures
 		runner announcer on: ResultAnnouncement do: [ :ann |
 			(ann result == result and: [ result runs = result total ]) ifTrue: [ model do: [
 				self assert: (self selectorSetOf: result errors) equals: #(fakeError) asSet.
-				self assert: (self selectorSetOf: result failures) equals: #(fakeErrorFailingInTearDown fakeFailure) asSet ] ] ].
+				self assert: (self selectorSetOf: result failures) equals: #(fakePassFailingInTearDown fakeErrorFailingInTearDown fakeFailure) asSet ] ] ].
 		runner run ]
 !
 

+ 13 - 9
lang/src/SUnit.js

@@ -1050,11 +1050,11 @@ selector: "execute:",
 protocol: "running",
 //>>excludeStart("ide", pragmas.excludeIdeData);
 args: ["aBlock"],
-source: "execute: aBlock\x0a\x09| failed result |\x0a\x09\x0a\x09testCase context: self.\x0a\x09[\x0a\x09\x09failed := true.\x0a\x09\x09result := aBlock value.\x0a\x09\x09testCase isAsync ifFalse: [\x0a\x09\x09\x09testCase assert: result isThenable not description: testCase asString, ' returned promise without sending #timeout:' ].\x0a\x09\x09failed := false\x0a\x09]\x0a\x09\x09ensure: [\x0a\x09\x09\x09\x22testCase context: nil.\x22\x0a\x09\x09\x09\x0a\x09\x09\x09(failed and: [ testCase isAsync ]) ifTrue: [ testCase finished ].\x0a\x09\x09\x09testCase isAsync\x0a\x09\x09\x09\x09ifFalse: [ testCase tearDown ]\x0a\x09\x09\x09\x09ifTrue: [ result isThenable ifTrue: [\x0a\x09\x09\x09\x09\x09result\x0a\x09\x09\x09\x09\x09\x09then: [ testCase isAsync ifTrue: [ self execute: [ testCase finished ] ] ]\x0a\x09\x09\x09\x09\x09\x09catch: [ :error | testCase isAsync ifTrue: [ self execute: [ (Smalltalk asSmalltalkException: error) pass ] ] ] ] ] ]",
+source: "execute: aBlock\x0a\x09| failed result |\x0a\x09\x0a\x09testCase context: self.\x0a\x09[\x0a\x09\x09failed := true.\x0a\x09\x09result := aBlock value.\x0a\x09\x09testCase isAsync ifFalse: [\x0a\x09\x09\x09testCase assert: result isThenable not description: testCase asString, ' returned promise without sending #timeout:' ].\x0a\x09\x09failed := false\x0a\x09]\x0a\x09\x09ensure: [\x0a\x09\x09\x09\x22testCase context: nil.\x22\x0a\x09\x09\x09\x0a\x09\x09\x09(failed and: [ testCase isAsync ]) ifTrue: [ testCase finished ].\x0a\x09\x09\x09testCase isAsync\x0a\x09\x09\x09\x09ifFalse: [ testCase tearDown ]\x0a\x09\x09\x09\x09ifTrue: [ result isThenable ifTrue: [\x0a\x09\x09\x09\x09\x09failed := false.\x0a\x09\x09\x09\x09\x09(result\x0a\x09\x09\x09\x09\x09\x09catch: [ :error | testCase isAsync ifTrue: [ self execute: [ failed := true. (Smalltalk asSmalltalkException: error) pass ] ] ])\x0a\x09\x09\x09\x09\x09\x09then: [ failed ifFalse: [ testCase isAsync ifTrue: [ self execute: [ testCase finished ] ] ] ] ] ] ]",
 referencedClasses: ["Smalltalk"],
 //>>excludeEnd("ide");
 pragmas: [],
-messageSends: ["context:", "ensure:", "value", "ifFalse:", "isAsync", "assert:description:", "not", "isThenable", ",", "asString", "ifTrue:", "and:", "finished", "ifFalse:ifTrue:", "tearDown", "then:catch:", "execute:", "pass", "asSmalltalkException:"]
+messageSends: ["context:", "ensure:", "value", "ifFalse:", "isAsync", "assert:description:", "not", "isThenable", ",", "asString", "ifTrue:", "and:", "finished", "ifFalse:ifTrue:", "tearDown", "then:", "catch:", "execute:", "pass", "asSmalltalkException:"]
 }, function ($methodClass){ return function (aBlock){
 var self=this,$self=this;
 var failed,result;
@@ -1111,7 +1111,8 @@ if($core.assert([$recv($self.testCase)._isAsync()
 //>>excludeEnd("ctx");
 ][0])){
 if($core.assert($recv(result)._isThenable())){
-return $recv(result)._then_catch_((function(){
+failed=false;
+return $recv($recv(result)._catch_((function(error){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx3) {
 //>>excludeEnd("ctx");
@@ -1124,7 +1125,8 @@ return [$self._execute_((function(){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx4) {
 //>>excludeEnd("ctx");
-return $recv($self.testCase)._finished();
+failed=true;
+return $recv($recv($globals.Smalltalk)._asSmalltalkException_(error))._pass();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 }, function($ctx4) {$ctx4.fillBlock({},$ctx3,11)});
 //>>excludeEnd("ctx");
@@ -1135,25 +1137,27 @@ return $recv($self.testCase)._finished();
 ][0];
 }
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx3) {$ctx3.fillBlock({},$ctx2,9)});
+}, function($ctx3) {$ctx3.fillBlock({error:error},$ctx2,9)});
 //>>excludeEnd("ctx");
-}),(function(error){
+})))._then_((function(){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx3) {
 //>>excludeEnd("ctx");
+if(!$core.assert(failed)){
 if($core.assert($recv($self.testCase)._isAsync())){
 return $self._execute_((function(){
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
 return $core.withContext(function($ctx4) {
 //>>excludeEnd("ctx");
-return $recv($recv($globals.Smalltalk)._asSmalltalkException_(error))._pass();
+return $recv($self.testCase)._finished();
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx4) {$ctx4.fillBlock({},$ctx3,14)});
+}, function($ctx4) {$ctx4.fillBlock({},$ctx3,15)});
 //>>excludeEnd("ctx");
 }));
 }
+}
 //>>excludeStart("ctx", pragmas.excludeDebugContexts);
-}, function($ctx3) {$ctx3.fillBlock({error:error},$ctx2,12)});
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,12)});
 //>>excludeEnd("ctx");
 }));
 }

+ 4 - 3
lang/src/SUnit.st

@@ -295,9 +295,10 @@ execute: aBlock
 			testCase isAsync
 				ifFalse: [ testCase tearDown ]
 				ifTrue: [ result isThenable ifTrue: [
-					result
-						then: [ testCase isAsync ifTrue: [ self execute: [ testCase finished ] ] ]
-						catch: [ :error | testCase isAsync ifTrue: [ self execute: [ (Smalltalk asSmalltalkException: error) pass ] ] ] ] ] ]
+					failed := false.
+					(result
+						catch: [ :error | testCase isAsync ifTrue: [ self execute: [ failed := true. (Smalltalk asSmalltalkException: error) pass ] ] ])
+						then: [ failed ifFalse: [ testCase isAsync ifTrue: [ self execute: [ testCase finished ] ] ] ] ] ] ]
 !
 
 start