Jelajahi Sumber

Merge branch 'master' into ast-interpreter

Conflicts:
	js/Kernel-Methods.deploy.js
	js/Kernel-Methods.js
	js/Kernel-Objects.deploy.js
	js/Kernel-Objects.js
	js/SUnit.deploy.js
	js/SUnit.js
Nicolas Petton 11 tahun lalu
induk
melakukan
ebe041c095

+ 55 - 9
js/Kernel-Methods.deploy.js

@@ -194,8 +194,10 @@ smalltalk.method({
 selector: "valueWithInterval:",
 fn: function (aNumber){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return setInterval(self, aNumber);
-;
+return smalltalk.withContext(function($ctx1) { 
+    	var interval = setInterval(self, aNumber);
+    	return smalltalk.Timeout._on_(interval);
+    ;
 return self}, self, "valueWithInterval:", [aNumber], smalltalk.BlockClosure)}
 }),
 smalltalk.BlockClosure);
@@ -218,8 +220,10 @@ smalltalk.method({
 selector: "valueWithTimeout:",
 fn: function (aNumber){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return setTimeout(self, aNumber);
-;
+return smalltalk.withContext(function($ctx1) { 
+    	var timeout = setTimeout(self, aNumber);
+    	return smalltalk.Timeout._on_(timeout);
+    ;
 return self}, self, "valueWithTimeout:", [aNumber], smalltalk.BlockClosure)}
 }),
 smalltalk.BlockClosure);
@@ -474,6 +478,19 @@ return self}, self, "addWorker", [], smalltalk.ForkPool)}
 }),
 smalltalk.ForkPool);
 
+smalltalk.addMethod(
+"_defaultMaxPoolSize",
+smalltalk.method({
+selector: "defaultMaxPoolSize",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(_st(self)._class())._defaultMaxPoolSize();
+return $1;
+}, self, "defaultMaxPoolSize", [], smalltalk.ForkPool)}
+}),
+smalltalk.ForkPool);
+
 smalltalk.addMethod(
 "_fork_",
 smalltalk.method({
@@ -481,7 +498,7 @@ selector: "fork:",
 fn: function (aBlock){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(self["@poolSize"]).__lt(self["@maxPoolSize"]);
+$1=_st(self["@poolSize"]).__lt(_st(self)._maxPoolSize());
 if(smalltalk.assert($1)){
 _st(self)._addWorker();
 };
@@ -498,7 +515,6 @@ fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
smalltalk.Object.fn.prototype._initialize.apply(_st(self), []);
 self["@poolSize"]=(0);
-self["@maxPoolSize"]=_st(_st(self)._class())._defaultMaxPoolSize();
 self["@queue"]=_st((smalltalk.Queue || Queue))._new();
 self["@worker"]=_st(self)._makeWorker();
 return self}, self, "initialize", [], smalltalk.ForkPool)}
@@ -536,6 +552,35 @@ return $1;
 }),
 smalltalk.ForkPool);
 
+smalltalk.addMethod(
+"_maxPoolSize",
+smalltalk.method({
+selector: "maxPoolSize",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$2=self["@maxPoolSize"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=_st(self)._defaultMaxPoolSize();
+} else {
+$1=$2;
+};
+return $1;
+}, self, "maxPoolSize", [], smalltalk.ForkPool)}
+}),
+smalltalk.ForkPool);
+
+smalltalk.addMethod(
+"_maxPoolSize_",
+smalltalk.method({
+selector: "maxPoolSize:",
+fn: function (anInteger){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@maxPoolSize"]=anInteger;
+return self}, self, "maxPoolSize:", [anInteger], smalltalk.ForkPool)}
+}),
+smalltalk.ForkPool);
+
 
 smalltalk.ForkPool.klass.iVarNames = ['default'];
 smalltalk.addMethod(
@@ -544,12 +589,13 @@ smalltalk.method({
 selector: "default",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-if(($receiver = self["@default"]) == nil || $receiver == undefined){
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$2=self["@default"];
+if(($receiver = $2) == nil || $receiver == undefined){
 self["@default"]=_st(self)._new();
 $1=self["@default"];
 } else {
-$1=self["@default"];
+$1=$2;
 };
 return $1;
 }, self, "default", [], smalltalk.ForkPool.klass)}

+ 77 - 15
js/Kernel-Methods.js

@@ -271,11 +271,13 @@ selector: "valueWithInterval:",
 category: 'timeout/interval',
 fn: function (aNumber){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return setInterval(self, aNumber);
-;
+return smalltalk.withContext(function($ctx1) { 
+    	var interval = setInterval(self, aNumber);
+    	return smalltalk.Timeout._on_(interval);
+    ;
 return self}, self, "valueWithInterval:", [aNumber], smalltalk.BlockClosure)},
 args: ["aNumber"],
-source: "valueWithInterval: aNumber\x0a\x09<return setInterval(self, aNumber)>",
+source: "valueWithInterval: aNumber\x0a\x09<\x0a    \x09var interval = setInterval(self, aNumber);\x0a    \x09return smalltalk.Timeout._on_(interval);\x0a    >",
 messageSends: [],
 referencedClasses: []
 }),
@@ -305,11 +307,13 @@ selector: "valueWithTimeout:",
 category: 'timeout/interval',
 fn: function (aNumber){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return setTimeout(self, aNumber);
-;
+return smalltalk.withContext(function($ctx1) { 
+    	var timeout = setTimeout(self, aNumber);
+    	return smalltalk.Timeout._on_(timeout);
+    ;
 return self}, self, "valueWithTimeout:", [aNumber], smalltalk.BlockClosure)},
 args: ["aNumber"],
-source: "valueWithTimeout: aNumber\x0a\x09<return setTimeout(self, aNumber)>",
+source: "valueWithTimeout: aNumber\x0a\x09<\x0a    \x09var timeout = setTimeout(self, aNumber);\x0a    \x09return smalltalk.Timeout._on_(timeout);\x0a    >",
 messageSends: [],
 referencedClasses: []
 }),
@@ -639,6 +643,7 @@ smalltalk.CompiledMethod);
 
 
 smalltalk.addClass('ForkPool', smalltalk.Object, ['poolSize', 'maxPoolSize', 'queue', 'worker'], 'Kernel-Methods');
+smalltalk.ForkPool.comment="A ForkPool is responsible for handling forked blocks.\x0aThe pool size sets the maximum concurrent forked blocks.\x0a\x0aThe default instance is accessed with `ForkPool default`"
 smalltalk.addMethod(
 "_addWorker",
 smalltalk.method({
@@ -656,6 +661,24 @@ referencedClasses: []
 }),
 smalltalk.ForkPool);
 
+smalltalk.addMethod(
+"_defaultMaxPoolSize",
+smalltalk.method({
+selector: "defaultMaxPoolSize",
+category: 'defaults',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(_st(self)._class())._defaultMaxPoolSize();
+return $1;
+}, self, "defaultMaxPoolSize", [], smalltalk.ForkPool)},
+args: [],
+source: "defaultMaxPoolSize\x0a\x09^ self class defaultMaxPoolSize",
+messageSends: ["defaultMaxPoolSize", "class"],
+referencedClasses: []
+}),
+smalltalk.ForkPool);
+
 smalltalk.addMethod(
 "_fork_",
 smalltalk.method({
@@ -664,15 +687,15 @@ category: 'action',
 fn: function (aBlock){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1;
-$1=_st(self["@poolSize"]).__lt(self["@maxPoolSize"]);
+$1=_st(self["@poolSize"]).__lt(_st(self)._maxPoolSize());
 if(smalltalk.assert($1)){
 _st(self)._addWorker();
 };
 _st(self["@queue"])._back_(aBlock);
 return self}, self, "fork:", [aBlock], smalltalk.ForkPool)},
 args: ["aBlock"],
-source: "fork: aBlock\x0a\x09poolSize < maxPoolSize ifTrue: [ self addWorker ].\x0a\x09queue back: aBlock",
-messageSends: ["ifTrue:", "addWorker", "<", "back:"],
+source: "fork: aBlock\x0a\x09poolSize < self maxPoolSize ifTrue: [ self addWorker ].\x0a\x09queue back: aBlock",
+messageSends: ["ifTrue:", "addWorker", "<", "maxPoolSize", "back:"],
 referencedClasses: []
 }),
 smalltalk.ForkPool);
@@ -686,13 +709,12 @@ fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
smalltalk.Object.fn.prototype._initialize.apply(_st(self), []);
 self["@poolSize"]=(0);
-self["@maxPoolSize"]=_st(_st(self)._class())._defaultMaxPoolSize();
 self["@queue"]=_st((smalltalk.Queue || Queue))._new();
 self["@worker"]=_st(self)._makeWorker();
 return self}, self, "initialize", [], smalltalk.ForkPool)},
 args: [],
-source: "initialize\x0a    super initialize.\x0a\x09poolSize := 0.\x0a    maxPoolSize := self class defaultMaxPoolSize.\x0a    queue := Queue new.\x0a    worker := self makeWorker",
-messageSends: ["initialize", "defaultMaxPoolSize", "class", "new", "makeWorker"],
+source: "initialize\x0a    super initialize.\x0a    \x0a\x09poolSize := 0.\x0a    queue := Queue new.\x0a    worker := self makeWorker",
+messageSends: ["initialize", "new", "makeWorker"],
 referencedClasses: ["Queue"]
 }),
 smalltalk.ForkPool);
@@ -733,6 +755,45 @@ referencedClasses: ["Object"]
 }),
 smalltalk.ForkPool);
 
+smalltalk.addMethod(
+"_maxPoolSize",
+smalltalk.method({
+selector: "maxPoolSize",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$2=self["@maxPoolSize"];
+if(($receiver = $2) == nil || $receiver == undefined){
+$1=_st(self)._defaultMaxPoolSize();
+} else {
+$1=$2;
+};
+return $1;
+}, self, "maxPoolSize", [], smalltalk.ForkPool)},
+args: [],
+source: "maxPoolSize\x0a\x09^ maxPoolSize ifNil: [ self defaultMaxPoolSize ]",
+messageSends: ["ifNil:", "defaultMaxPoolSize"],
+referencedClasses: []
+}),
+smalltalk.ForkPool);
+
+smalltalk.addMethod(
+"_maxPoolSize_",
+smalltalk.method({
+selector: "maxPoolSize:",
+category: 'accessing',
+fn: function (anInteger){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@maxPoolSize"]=anInteger;
+return self}, self, "maxPoolSize:", [anInteger], smalltalk.ForkPool)},
+args: ["anInteger"],
+source: "maxPoolSize: anInteger\x0a\x09maxPoolSize := anInteger",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ForkPool);
+
 
 smalltalk.ForkPool.klass.iVarNames = ['default'];
 smalltalk.addMethod(
@@ -742,12 +803,13 @@ selector: "default",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-if(($receiver = self["@default"]) == nil || $receiver == undefined){
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$2=self["@default"];
+if(($receiver = $2) == nil || $receiver == undefined){
 self["@default"]=_st(self)._new();
 $1=self["@default"];
 } else {
-$1=self["@default"];
+$1=$2;
 };
 return $1;
 }, self, "default", [], smalltalk.ForkPool.klass)},

+ 58 - 24
js/Kernel-Objects.deploy.js

@@ -2055,30 +2055,6 @@ return $1;
 }),
 smalltalk.Number);
 
-smalltalk.addMethod(
-"_clearInterval",
-smalltalk.method({
-selector: "clearInterval",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
clearInterval(Number(self));
-;
-return self}, self, "clearInterval", [], smalltalk.Number)}
-}),
-smalltalk.Number);
-
-smalltalk.addMethod(
-"_clearTimeout",
-smalltalk.method({
-selector: "clearTimeout",
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
clearTimeout(Number(self));
-;
-return self}, self, "clearTimeout", [], smalltalk.Number)}
-}),
-smalltalk.Number);
-
 smalltalk.addMethod(
 "_copy",
 smalltalk.method({
@@ -3415,6 +3391,64 @@ return self}, self, "current", [], smalltalk.Smalltalk.klass)}
 smalltalk.Smalltalk.klass);
 
 
+smalltalk.addClass('Timeout', smalltalk.Object, ['rawTimeout'], 'Kernel-Objects');
+smalltalk.addMethod(
+"_clearInterval",
+smalltalk.method({
+selector: "clearInterval",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+    	var interval = self["@rawTimeout"];
+		clearInterval(interval);
+    ;
+return self}, self, "clearInterval", [], smalltalk.Timeout)}
+}),
+smalltalk.Timeout);
+
+smalltalk.addMethod(
+"_clearTimeout",
+smalltalk.method({
+selector: "clearTimeout",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+    	var timeout = self["@rawTimeout"];
+		clearTimeout(timeout);
+    ;
+return self}, self, "clearTimeout", [], smalltalk.Timeout)}
+}),
+smalltalk.Timeout);
+
+smalltalk.addMethod(
+"_rawTimeout_",
+smalltalk.method({
+selector: "rawTimeout:",
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@rawTimeout"]=anObject;
+return self}, self, "rawTimeout:", [anObject], smalltalk.Timeout)}
+}),
+smalltalk.Timeout);
+
+
+smalltalk.addMethod(
+"_on_",
+smalltalk.method({
+selector: "on:",
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
+$2=_st(self)._new();
+_st($2)._rawTimeout_(anObject);
+$3=_st($2)._yourself();
+$1=$3;
+return $1;
+}, self, "on:", [anObject], smalltalk.Timeout.klass)}
+}),
+smalltalk.Timeout.klass);
+
+
 smalltalk.addClass('UndefinedObject', smalltalk.Object, [], 'Kernel-Objects');
 smalltalk.addMethod(
 "_asJSON",

+ 79 - 34
js/Kernel-Objects.js

@@ -2851,40 +2851,6 @@ referencedClasses: ["Random"]
 }),
 smalltalk.Number);
 
-smalltalk.addMethod(
-"_clearInterval",
-smalltalk.method({
-selector: "clearInterval",
-category: 'timeouts/intervals',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
clearInterval(Number(self));
-;
-return self}, self, "clearInterval", [], smalltalk.Number)},
-args: [],
-source: "clearInterval\x0a\x09<clearInterval(Number(self))>",
-messageSends: [],
-referencedClasses: []
-}),
-smalltalk.Number);
-
-smalltalk.addMethod(
-"_clearTimeout",
-smalltalk.method({
-selector: "clearTimeout",
-category: 'timeouts/intervals',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
clearTimeout(Number(self));
-;
-return self}, self, "clearTimeout", [], smalltalk.Number)},
-args: [],
-source: "clearTimeout\x0a\x09<clearTimeout(Number(self))>",
-messageSends: [],
-referencedClasses: []
-}),
-smalltalk.Number);
-
 smalltalk.addMethod(
 "_copy",
 smalltalk.method({
@@ -4680,6 +4646,85 @@ referencedClasses: []
 smalltalk.Smalltalk.klass);
 
 
+smalltalk.addClass('Timeout', smalltalk.Object, ['rawTimeout'], 'Kernel-Objects');
+smalltalk.Timeout.comment="I am wrapping the returns from set{Timeout,Interval}.\x0a\x0aNumber suffices in browsers, but node.js returns an object."
+smalltalk.addMethod(
+"_clearInterval",
+smalltalk.method({
+selector: "clearInterval",
+category: 'timeout/interval',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+    	var interval = self["@rawTimeout"];
+		clearInterval(interval);
+    ;
+return self}, self, "clearInterval", [], smalltalk.Timeout)},
+args: [],
+source: "clearInterval\x0a\x09<\x0a    \x09var interval = self[\x22@rawTimeout\x22];\x0a\x09\x09clearInterval(interval);\x0a    >",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Timeout);
+
+smalltalk.addMethod(
+"_clearTimeout",
+smalltalk.method({
+selector: "clearTimeout",
+category: 'timeout/interval',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+    	var timeout = self["@rawTimeout"];
+		clearTimeout(timeout);
+    ;
+return self}, self, "clearTimeout", [], smalltalk.Timeout)},
+args: [],
+source: "clearTimeout\x0a\x09<\x0a    \x09var timeout = self[\x22@rawTimeout\x22];\x0a\x09\x09clearTimeout(timeout);\x0a    >",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Timeout);
+
+smalltalk.addMethod(
+"_rawTimeout_",
+smalltalk.method({
+selector: "rawTimeout:",
+category: 'accessing',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@rawTimeout"]=anObject;
+return self}, self, "rawTimeout:", [anObject], smalltalk.Timeout)},
+args: ["anObject"],
+source: "rawTimeout: anObject\x0a\x09rawTimeout := anObject",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.Timeout);
+
+
+smalltalk.addMethod(
+"_on_",
+smalltalk.method({
+selector: "on:",
+category: 'instance creation',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
+$2=_st(self)._new();
+_st($2)._rawTimeout_(anObject);
+$3=_st($2)._yourself();
+$1=$3;
+return $1;
+}, self, "on:", [anObject], smalltalk.Timeout.klass)},
+args: ["anObject"],
+source: "on: anObject\x0a\x09^self new rawTimeout: anObject; yourself",
+messageSends: ["rawTimeout:", "new", "yourself"],
+referencedClasses: []
+}),
+smalltalk.Timeout.klass);
+
+
 smalltalk.addClass('UndefinedObject', smalltalk.Object, [], 'Kernel-Objects');
 smalltalk.UndefinedObject.comment="UndefinedObject describes the behavior of its sole instance, `nil`. `nil` represents a prior value for variables that have not been initialized, or for results which are meaningless.\x0a\x0a`nil` is the Smalltalk representation of the `undefined` JavaScript object."
 smalltalk.addMethod(

+ 237 - 154
js/Kernel-Tests.deploy.js

@@ -1,5 +1,35 @@
 smalltalk.addPackage('Kernel-Tests', {});
 smalltalk.addClass('BlockClosureTest', smalltalk.TestCase, [], 'Kernel-Tests');
+smalltalk.addMethod(
+"_testCanClearInterval",
+smalltalk.method({
+selector: "testCanClearInterval",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._shouldnt_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(_st((function(){
+return smalltalk.withContext(function($ctx3) { 
return _st(_st((smalltalk.Error || Error))._new())._signal();
+})}))._valueWithInterval_((0)))._clearInterval();
+})}),(smalltalk.Error || Error));
+return self}, self, "testCanClearInterval", [], smalltalk.BlockClosureTest)}
+}),
+smalltalk.BlockClosureTest);
+
+smalltalk.addMethod(
+"_testCanClearTimeout",
+smalltalk.method({
+selector: "testCanClearTimeout",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._shouldnt_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(_st((function(){
+return smalltalk.withContext(function($ctx3) { 
return _st(_st((smalltalk.Error || Error))._new())._signal();
+})}))._valueWithTimeout_((0)))._clearTimeout();
+})}),(smalltalk.Error || Error));
+return self}, self, "testCanClearTimeout", [], smalltalk.BlockClosureTest)}
+}),
+smalltalk.BlockClosureTest);
+
 smalltalk.addMethod(
 "_testCompiledSource",
 smalltalk.method({
@@ -121,7 +151,7 @@ smalltalk.method({
 selector: "testWhileFalse",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.i=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.i=nil;
 $ctx1.locals.i=(0);
 _st((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st($ctx1.locals.i).__gt((5));
@@ -147,7 +177,7 @@ smalltalk.method({
 selector: "testWhileTrue",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.i=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.i=nil;
 $ctx1.locals.i=(0);
 _st((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st($ctx1.locals.i).__lt((5));
@@ -216,47 +246,63 @@ smalltalk.method({
 selector: "testIfTrueIfFalse",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4,$5,$6,$7,$8;
+return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2,$4,$6,$5,$7,$9,$8,$10,$12,$11,$13,$15,$14,$16,$18,$17,$19,$21,$20,$22,$24,$23;
+$1=self;
 if(smalltalk.assert(true)){
-$1="alternative block";
+$3="alternative block";
 };
-_st(self)._assert_(_st($1).__eq("alternative block"));
+$2=_st($3).__eq("alternative block");
+_st($1)._assert_($2);
+$4=self;
 if(! smalltalk.assert(true)){
-$2="alternative block";
+$6="alternative block";
 };
-_st(self)._assert_(_st($2).__eq(nil));
+$5=_st($6).__eq(nil);
+_st($4)._assert_($5);
+$7=self;
 if(smalltalk.assert(false)){
-$3="alternative block";
+$9="alternative block";
 };
-_st(self)._assert_(_st($3).__eq(nil));
+$8=_st($9).__eq(nil);
+_st($7)._assert_($8);
+$10=self;
 if(! smalltalk.assert(false)){
-$4="alternative block";
+$12="alternative block";
 };
-_st(self)._assert_(_st($4).__eq("alternative block"));
+$11=_st($12).__eq("alternative block");
+_st($10)._assert_($11);
+$13=self;
 if(smalltalk.assert(false)){
-$5="alternative block";
+$15="alternative block";
 } else {
-$5="alternative block2";
+$15="alternative block2";
 };
-_st(self)._assert_(_st($5).__eq("alternative block2"));
+$14=_st($15).__eq("alternative block2");
+_st($13)._assert_($14);
+$16=self;
 if(smalltalk.assert(false)){
-$6="alternative block2";
+$18="alternative block2";
 } else {
-$6="alternative block";
+$18="alternative block";
 };
-_st(self)._assert_(_st($6).__eq("alternative block"));
+$17=_st($18).__eq("alternative block");
+_st($16)._assert_($17);
+$19=self;
 if(smalltalk.assert(true)){
-$7="alternative block";
+$21="alternative block";
 } else {
-$7="alternative block2";
+$21="alternative block2";
 };
-_st(self)._assert_(_st($7).__eq("alternative block"));
+$20=_st($21).__eq("alternative block");
+_st($19)._assert_($20);
+$22=self;
 if(smalltalk.assert(true)){
-$8="alternative block2";
+$24="alternative block2";
 } else {
-$8="alternative block";
+$24="alternative block";
 };
-_st(self)._assert_(_st($8).__eq("alternative block2"));
+$23=_st($24).__eq("alternative block2");
+_st($22)._assert_($23);
 return self}, self, "testIfTrueIfFalse", [], smalltalk.BooleanTest)}
 }),
 smalltalk.BooleanTest);
@@ -342,21 +388,25 @@ smalltalk.method({
 selector: "testLogic",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
-_st(self)._assert_(_st(true).__and(true));
-_st(self)._deny_(_st(true).__and(false));
-_st(self)._deny_(_st(false).__and(true));
-$1=_st(self)._deny_(_st(false).__and(false));
-_st(self)._assert_(_st(true).__or(true));
-_st(self)._assert_(_st(true).__or(false));
-_st(self)._assert_(_st(false).__or(true));
-$2=_st(self)._deny_(_st(false).__or(false));
-_st(self)._assert_(_st(true).__and(_st((1)).__gt((0))));
-_st(self)._deny_(_st(_st((1)).__gt((0))).__and(false));
-$3=_st(self)._deny_(_st(_st((1)).__gt((0))).__and(_st((1)).__gt((2))));
-_st(self)._assert_(_st(false).__or(_st((1)).__gt((0))));
-_st(self)._assert_(_st(_st((1)).__gt((0))).__or(false));
-$4=_st(self)._assert_(_st(_st((1)).__gt((0))).__or(_st((1)).__gt((2))));
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4,$5,$6,$7,$8;
+$1=self;
+_st($1)._assert_(_st(true).__and(true));
+_st($1)._deny_(_st(true).__and(false));
+_st($1)._deny_(_st(false).__and(true));
+$2=_st($1)._deny_(_st(false).__and(false));
+$3=self;
+_st($3)._assert_(_st(true).__or(true));
+_st($3)._assert_(_st(true).__or(false));
+_st($3)._assert_(_st(false).__or(true));
+$4=_st($3)._deny_(_st(false).__or(false));
+$5=self;
+_st($5)._assert_(_st(true).__and(_st((1)).__gt((0))));
+_st($5)._deny_(_st(_st((1)).__gt((0))).__and(false));
+$6=_st($5)._deny_(_st(_st((1)).__gt((0))).__and(_st((1)).__gt((2))));
+$7=self;
+_st($7)._assert_(_st(false).__or(_st((1)).__gt((0))));
+_st($7)._assert_(_st(_st((1)).__gt((0))).__or(false));
+$8=_st($7)._assert_(_st(_st((1)).__gt((0))).__or(_st((1)).__gt((2))));
 return self}, self, "testLogic", [], smalltalk.BooleanTest)}
 }),
 smalltalk.BooleanTest);
@@ -367,47 +417,51 @@ smalltalk.method({
 selector: "testLogicKeywords",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
-_st(self)._assert_(_st(true)._and_((function(){
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4,$5,$6,$7,$8;
+$1=self;
+_st($1)._assert_(_st(true)._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return true;
 })})));
-_st(self)._deny_(_st(true)._and_((function(){
+_st($1)._deny_(_st(true)._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return false;
 })})));
-_st(self)._deny_(_st(false)._and_((function(){
+_st($1)._deny_(_st(false)._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return true;
 })})));
-$1=_st(self)._deny_(_st(false)._and_((function(){
+$2=_st($1)._deny_(_st(false)._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return false;
 })})));
-_st(self)._assert_(_st(true)._or_((function(){
+$3=self;
+_st($3)._assert_(_st(true)._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return true;
 })})));
-_st(self)._assert_(_st(true)._or_((function(){
+_st($3)._assert_(_st(true)._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return false;
 })})));
-_st(self)._assert_(_st(false)._or_((function(){
+_st($3)._assert_(_st(false)._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return true;
 })})));
-$2=_st(self)._deny_(_st(false)._or_((function(){
+$4=_st($3)._deny_(_st(false)._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return false;
 })})));
-_st(self)._assert_(_st(true)._and_((function(){
+$5=self;
+_st($5)._assert_(_st(true)._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st((1)).__gt((0));
 })})));
-_st(self)._deny_(_st(_st((1)).__gt((0)))._and_((function(){
+_st($5)._deny_(_st(_st((1)).__gt((0)))._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return false;
 })})));
-$3=_st(self)._deny_(_st(_st((1)).__gt((0)))._and_((function(){
+$6=_st($5)._deny_(_st(_st((1)).__gt((0)))._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st((1)).__gt((2));
 })})));
-_st(self)._assert_(_st(false)._or_((function(){
+$7=self;
+_st($7)._assert_(_st(false)._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st((1)).__gt((0));
 })})));
-_st(self)._assert_(_st(_st((1)).__gt((0)))._or_((function(){
+_st($7)._assert_(_st(_st((1)).__gt((0)))._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return false;
 })})));
-$4=_st(self)._assert_(_st(_st((1)).__gt((0)))._or_((function(){
+$8=_st($7)._assert_(_st(_st((1)).__gt((0)))._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st((1)).__gt((2));
 })})));
 return self}, self, "testLogicKeywords", [], smalltalk.BooleanTest)}
@@ -452,8 +506,10 @@ smalltalk.method({
 selector: "tearDown",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
if(($receiver = self["@theClass"]) == nil || $receiver == undefined){
-self["@theClass"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@theClass"];
+if(($receiver = $1) == nil || $receiver == undefined){
+$1;
 } else {
 _st(_st((smalltalk.Smalltalk || Smalltalk))._current())._removeClass_(self["@theClass"]);
 self["@theClass"]=nil;
@@ -596,8 +652,8 @@ smalltalk.method({
 selector: "testAsSet",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.c=nil;
-$ctx1.set=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.c=nil;
+$ctx1.locals.set=nil;
 $ctx1.locals.c=_st(self)._collectionWithDuplicates();
 $ctx1.locals.set=_st($ctx1.locals.c)._asSet();
 _st(self)._assert_(_st(_st($ctx1.locals.set)._size()).__eq((5)));
@@ -614,7 +670,7 @@ smalltalk.method({
 selector: "testCollect",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection=[(1), (2), (3), (4)];
 _st(self)._assertSameContents_as_(_st(_st(self)._collection())._collect_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st(each)._abs();
@@ -647,7 +703,7 @@ smalltalk.method({
 selector: "testDo",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection=_st((smalltalk.OrderedCollection || OrderedCollection))._new();
 _st(_st(self)._collection())._do_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st($ctx1.locals.newCollection)._add_(each);
@@ -675,7 +731,7 @@ smalltalk.method({
 selector: "testSelect",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection=[(2), (-4)];
 _st(self)._assertSameContents_as_(_st(_st(self)._collection())._select_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st(each)._even();
@@ -756,7 +812,9 @@ smalltalk.method({
 selector: "collectionClass",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return (smalltalk.HashedCollection || HashedCollection);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=(smalltalk.HashedCollection || HashedCollection);
+return $1;
 }, self, "collectionClass", [], smalltalk.HashedCollectionTest.klass)}
 }),
 smalltalk.HashedCollectionTest.klass);
@@ -810,7 +868,7 @@ smalltalk.method({
 selector: "testAccessing",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_("hello","world");
 _st(self)._assert_(_st(_st($ctx1.locals.d)._at_("hello")).__eq("world"));
@@ -846,8 +904,8 @@ selector: "testEquality",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4,$5,$6,$7,$8,$9,$10;
-$ctx1.d1=nil;
-$ctx1.d2=nil;
+$ctx1.locals.d1=nil;
+$ctx1.locals.d2=nil;
 _st(self)._assert_(_st(_st((smalltalk.Dictionary || Dictionary))._new()).__eq(_st((smalltalk.Dictionary || Dictionary))._new()));
 $1=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($1)._at_put_((1),(2));
@@ -884,8 +942,8 @@ smalltalk.method({
 selector: "testIfAbsent",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
-$ctx1.visited=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
+$ctx1.locals.visited=nil;
 $ctx1.locals.visited=false;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_ifAbsent_("hello",(function(){
@@ -903,9 +961,9 @@ smalltalk.method({
 selector: "testIfPresent",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
-$ctx1.visited=nil;
-$ctx1.absent=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
+$ctx1.locals.visited=nil;
+$ctx1.locals.absent=nil;
 $ctx1.locals.visited=false;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_("hello","world");
@@ -929,8 +987,8 @@ smalltalk.method({
 selector: "testIfPresentIfAbsent",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
-$ctx1.visited=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
+$ctx1.locals.visited=nil;
 $ctx1.locals.visited=false;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_("hello","world");
@@ -960,7 +1018,7 @@ smalltalk.method({
 selector: "testKeys",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_((1),(2));
 _st($ctx1.locals.d)._at_put_((2),(3));
@@ -992,8 +1050,8 @@ smalltalk.method({
 selector: "testRemoveKey",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
-$ctx1.key=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
+$ctx1.locals.key=nil;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_((1),(2));
 _st($ctx1.locals.d)._at_put_((2),(3));
@@ -1014,8 +1072,8 @@ smalltalk.method({
 selector: "testRemoveKeyIfAbsent",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
-$ctx1.key=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
+$ctx1.locals.key=nil;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_((1),(2));
 _st($ctx1.locals.d)._at_put_((2),(3));
@@ -1040,7 +1098,7 @@ smalltalk.method({
 selector: "testSize",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st(self)._assert_(_st(_st($ctx1.locals.d)._size()).__eq((0)));
 _st($ctx1.locals.d)._at_put_((1),(2));
@@ -1057,7 +1115,7 @@ smalltalk.method({
 selector: "testValues",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_((1),(2));
 _st($ctx1.locals.d)._at_put_((2),(3));
@@ -1074,7 +1132,9 @@ smalltalk.method({
 selector: "collectionClass",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return (smalltalk.Dictionary || Dictionary);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=(smalltalk.Dictionary || Dictionary);
+return $1;
 }, self, "collectionClass", [], smalltalk.DictionaryTest.klass)}
 }),
 smalltalk.DictionaryTest.klass);
@@ -1117,7 +1177,7 @@ smalltalk.method({
 selector: "testAtIfAbsent",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.array=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.array=nil;
 $ctx1.locals.array=["hello", "world"];
 _st(self)._assert_equals_(_st($ctx1.locals.array)._at_((1)),"hello");
 _st(self)._assert_equals_(_st($ctx1.locals.array)._at_((2)),"world");
@@ -1167,17 +1227,19 @@ smalltalk.method({
 selector: "testPrintString",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
-$ctx1.array=nil;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
+$ctx1.locals.array=nil;
 $ctx1.locals.array=_st((smalltalk.Array || Array))._new();
 _st(self)._assert_equals_("a Array ()",_st($ctx1.locals.array)._printString());
-_st($ctx1.locals.array)._add_((1));
-$1=_st($ctx1.locals.array)._add_((3));
+$1=$ctx1.locals.array;
+_st($1)._add_((1));
+$2=_st($1)._add_((3));
 _st(self)._assert_equals_("a Array (1 3)",_st($ctx1.locals.array)._printString());
 _st($ctx1.locals.array)._add_("foo");
 _st(self)._assert_equals_("a Array (1 3 'foo')",_st($ctx1.locals.array)._printString());
-_st($ctx1.locals.array)._remove_((1));
-$2=_st($ctx1.locals.array)._remove_((3));
+$3=$ctx1.locals.array;
+_st($3)._remove_((1));
+$4=_st($3)._remove_((3));
 _st(self)._assert_equals_("a Array ('foo')",_st($ctx1.locals.array)._printString());
 _st($ctx1.locals.array)._addLast_((3));
 _st(self)._assert_equals_("a Array ('foo' 3)",_st($ctx1.locals.array)._printString());
@@ -1194,7 +1256,9 @@ smalltalk.method({
 selector: "collectionClass",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return (smalltalk.Array || Array);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=(smalltalk.Array || Array);
+return $1;
 }, self, "collectionClass", [], smalltalk.ArrayTest.klass)}
 }),
 smalltalk.ArrayTest.klass);
@@ -1284,7 +1348,7 @@ smalltalk.method({
 selector: "testCollect",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection="hheelllloo";
 _st(self)._assertSameContents_as_(_st(_st(self)._collection())._collect_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st(each).__comma(each);
@@ -1381,7 +1445,7 @@ smalltalk.method({
 selector: "testSelect",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection="o";
 _st(self)._assertSameContents_as_(_st(_st(self)._collection())._select_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st(each).__eq("o");
@@ -1408,12 +1472,13 @@ smalltalk.method({
 selector: "testStreamContents",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 _st(self)._assert_equals_("hello world",_st((smalltalk.String || String))._streamContents_((function(aStream){
-return smalltalk.withContext(function($ctx2) { 
_st(aStream)._nextPutAll_("hello");
-_st(aStream)._space();
-$1=_st(aStream)._nextPutAll_("world");
-return $1;
+return smalltalk.withContext(function($ctx2) { 
$1=aStream;
+_st($1)._nextPutAll_("hello");
+_st($1)._space();
+$2=_st($1)._nextPutAll_("world");
+return $2;
 })})));
 return self}, self, "testStreamContents", [], smalltalk.StringTest)}
 }),
@@ -1426,7 +1491,9 @@ smalltalk.method({
 selector: "collectionClass",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return (smalltalk.String || String);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=(smalltalk.String || String);
+return $1;
 }, self, "collectionClass", [], smalltalk.StringTest.klass)}
 }),
 smalltalk.StringTest.klass);
@@ -1511,7 +1578,7 @@ smalltalk.method({
 selector: "testCollect",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection=smalltalk.symbolFor("hheelllloo");
 _st(self)._assertSameContents_as_(_st(_st(self)._collection())._collect_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st(each).__comma(each);
@@ -1630,7 +1697,7 @@ smalltalk.method({
 selector: "testSelect",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection="o";
 _st(self)._assertSameContents_as_(_st(_st(self)._collection())._select_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st(each).__eq("o");
@@ -1658,7 +1725,9 @@ smalltalk.method({
 selector: "collectionClass",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return (smalltalk.Symbol || Symbol);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=(smalltalk.Symbol || Symbol);
+return $1;
 }, self, "collectionClass", [], smalltalk.SymbolTest.klass)}
 }),
 smalltalk.SymbolTest.klass);
@@ -1672,7 +1741,6 @@ selector: "jsObject",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
return jsObject = {a: 1, b: function() {return 2;}, c: function(object) {return object;}, d: '', 'e': null};
-;
 return self}, self, "jsObject", [], smalltalk.JSObjectProxyTest)}
 }),
 smalltalk.JSObjectProxyTest);
@@ -1731,7 +1799,7 @@ smalltalk.method({
 selector: "testPropertyThatReturnsEmptyString",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.object=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.object=nil;
 $ctx1.locals.object=_st(self)._jsObject();
 _st(self)._assert_equals_("",_st($ctx1.locals.object)._d());
 _st($ctx1.locals.object)._d_("hello");
@@ -1746,7 +1814,7 @@ smalltalk.method({
 selector: "testPropertyThatReturnsUndefined",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.object=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.object=nil;
 $ctx1.locals.object=_st(self)._jsObject();
 _st(self)._shouldnt_raise_((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st($ctx1.locals.object)._e();
@@ -1763,7 +1831,7 @@ selector: "testYourself",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1,$2;
-$ctx1.object=nil;
+$ctx1.locals.object=nil;
 $1=_st(self)._jsObject();
 _st($1)._d_("test");
 $2=_st($1)._yourself();
@@ -2111,7 +2179,7 @@ smalltalk.method({
 selector: "testTimesRepeat",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.i=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.i=nil;
 $ctx1.locals.i=(0);
 _st((0))._timesRepeat_((function(){
 return smalltalk.withContext(function($ctx2) { 
$ctx1.locals.i=_st($ctx1.locals.i).__plus((1));
@@ -2174,7 +2242,9 @@ smalltalk.method({
 selector: "foo",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@foo"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@foo"];
+return $1;
 }, self, "foo", [], smalltalk.ObjectMock)}
 }),
 smalltalk.ObjectMock);
@@ -2200,7 +2270,6 @@ selector: "notDefined",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
return undefined;;
-;
 return self}, self, "notDefined", [], smalltalk.ObjectTest)}
 }),
 smalltalk.ObjectTest);
@@ -2211,7 +2280,7 @@ smalltalk.method({
 selector: "testBasicAccess",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o=nil;
 $ctx1.locals.o=_st((smalltalk.Object || Object))._new();
 _st($ctx1.locals.o)._basicAt_put_("a",(1));
 _st(self)._assert_equals_(_st($ctx1.locals.o)._basicAt_("a"),(1));
@@ -2226,7 +2295,7 @@ smalltalk.method({
 selector: "testBasicPerform",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o=nil;
 $ctx1.locals.o=_st((smalltalk.Object || Object))._new();
 _st($ctx1.locals.o)._basicAt_put_("func",(function(){
 return smalltalk.withContext(function($ctx2) { 
return "hello";
@@ -2259,7 +2328,7 @@ smalltalk.method({
 selector: "testEquality",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o=nil;
 $ctx1.locals.o=_st((smalltalk.Object || Object))._new();
 _st(self)._deny_(_st($ctx1.locals.o).__eq(_st((smalltalk.Object || Object))._new()));
 _st(self)._assert_(_st($ctx1.locals.o).__eq($ctx1.locals.o));
@@ -2288,7 +2357,7 @@ smalltalk.method({
 selector: "testIdentity",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o=nil;
 $ctx1.locals.o=_st((smalltalk.Object || Object))._new();
 _st(self)._deny_(_st($ctx1.locals.o).__eq_eq(_st((smalltalk.Object || Object))._new()));
 _st(self)._assert_(_st($ctx1.locals.o).__eq_eq($ctx1.locals.o));
@@ -2304,29 +2373,35 @@ smalltalk.method({
 selector: "testIfNil",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$1,$4,$3,$6,$5;
+return smalltalk.withContext(function($ctx1) { 
var $1,$4,$3,$2,$5,$8,$7,$6,$9,$12,$11,$10;
 _st(self)._deny_(_st(_st((smalltalk.Object || Object))._new())._isNil());
-$2=_st((smalltalk.Object || Object))._new();
-if(($receiver = $2) == nil || $receiver == undefined){
-$1=true;
-} else {
-$1=$2;
-};
-_st(self)._deny_(_st($1).__eq(true));
+$1=self;
 $4=_st((smalltalk.Object || Object))._new();
 if(($receiver = $4) == nil || $receiver == undefined){
+$3=true;
+} else {
 $3=$4;
+};
+$2=_st($3).__eq(true);
+_st($1)._deny_($2);
+$5=self;
+$8=_st((smalltalk.Object || Object))._new();
+if(($receiver = $8) == nil || $receiver == undefined){
+$7=$8;
 } else {
-$3=true;
+$7=true;
 };
-_st(self)._assert_(_st($3).__eq(true));
-$6=_st((smalltalk.Object || Object))._new();
-if(($receiver = $6) == nil || $receiver == undefined){
-$5=false;
+$6=_st($7).__eq(true);
+_st($5)._assert_($6);
+$9=self;
+$12=_st((smalltalk.Object || Object))._new();
+if(($receiver = $12) == nil || $receiver == undefined){
+$11=false;
 } else {
-$5=true;
+$11=true;
 };
-_st(self)._assert_(_st($5).__eq(true));
+$10=_st($11).__eq(true);
+_st($9)._assert_($10);
 _st(self)._assert_(_st(_st(_st((smalltalk.Object || Object))._new())._ifNotNil_ifNil_((function(){
 return smalltalk.withContext(function($ctx2) { 
return true;
 })}),(function(){
@@ -2342,7 +2417,7 @@ smalltalk.method({
 selector: "testInstVars",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o=nil;
 $ctx1.locals.o=_st((smalltalk.ObjectMock || ObjectMock))._new();
 _st(self)._assert_equals_(_st($ctx1.locals.o)._instVarAt_(smalltalk.symbolFor("foo")),nil);
 _st($ctx1.locals.o)._instVarAt_put_(smalltalk.symbolFor("foo"),(1));
@@ -2369,7 +2444,7 @@ smalltalk.method({
 selector: "testYourself",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o=nil;
 $ctx1.locals.o=_st((smalltalk.ObjectMock || ObjectMock))._new();
 _st(self)._assert_(_st(_st($ctx1.locals.o)._yourself()).__eq_eq($ctx1.locals.o));
 return self}, self, "testYourself", [], smalltalk.ObjectTest)}
@@ -2382,8 +2457,8 @@ smalltalk.method({
 selector: "testidentityHash",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o1=nil;
-$ctx1.o2=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o1=nil;
+$ctx1.locals.o2=nil;
 $ctx1.locals.o1=_st((smalltalk.Object || Object))._new();
 $ctx1.locals.o2=_st((smalltalk.Object || Object))._new();
 _st(self)._assert_(_st(_st($ctx1.locals.o1)._identityHash()).__eq_eq(_st($ctx1.locals.o1)._identityHash()));
@@ -2422,9 +2497,10 @@ smalltalk.method({
 selector: "tearDown",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-_st((smalltalk.Package || Package))._defaultCommitPathJs_(self["@backUpCommitPathJs"]);
-$1=_st((smalltalk.Package || Package))._defaultCommitPathSt_(self["@backUpCommitPathSt"]);
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+$1=(smalltalk.Package || Package);
+_st($1)._defaultCommitPathJs_(self["@backUpCommitPathJs"]);
+$2=_st($1)._defaultCommitPathSt_(self["@backUpCommitPathSt"]);
 return self}, self, "tearDown", [], smalltalk.PackageTest)}
 }),
 smalltalk.PackageTest);
@@ -2482,10 +2558,11 @@ smalltalk.method({
 selector: "setUp",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 smalltalk.PackageTest.fn.prototype._setUp.apply(_st(self), []);
-_st((smalltalk.Package || Package))._defaultCommitPathJs_("javascripts/");
-$1=_st((smalltalk.Package || Package))._defaultCommitPathSt_("smalltalk/");
+$1=(smalltalk.Package || Package);
+_st($1)._defaultCommitPathJs_("javascripts/");
+$2=_st($1)._defaultCommitPathSt_("smalltalk/");
 return self}, self, "setUp", [], smalltalk.PackageWithDefaultCommitPathChangedTest)}
 }),
 smalltalk.PackageWithDefaultCommitPathChangedTest);
@@ -2623,8 +2700,8 @@ selector: "textNext",
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
_st((10000))._timesRepeat_((function(){
-return smalltalk.withContext(function($ctx2) { 
$ctx2.current=nil;
-$ctx2.next=nil;
+return smalltalk.withContext(function($ctx2) { 
$ctx2.locals.current=nil;
+$ctx2.locals.next=nil;
 $ctx2.locals.next=_st(_st((smalltalk.Random || Random))._new())._next();
 $ctx2.locals.next;
 _st(self)._assert_(_st($ctx2.locals.next).__gt_eq((0)));
@@ -2645,7 +2722,7 @@ smalltalk.method({
 selector: "testAddRemove",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.set=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.set=nil;
 $ctx1.locals.set=_st((smalltalk.Set || Set))._new();
 _st(self)._assert_(_st($ctx1.locals.set)._isEmpty());
 _st($ctx1.locals.set)._add_((3));
@@ -2677,17 +2754,19 @@ smalltalk.method({
 selector: "testPrintString",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
-$ctx1.set=nil;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
+$ctx1.locals.set=nil;
 $ctx1.locals.set=_st((smalltalk.Set || Set))._new();
 _st(self)._assert_equals_("a Set ()",_st($ctx1.locals.set)._printString());
-_st($ctx1.locals.set)._add_((1));
-$1=_st($ctx1.locals.set)._add_((3));
+$1=$ctx1.locals.set;
+_st($1)._add_((1));
+$2=_st($1)._add_((3));
 _st(self)._assert_equals_("a Set (1 3)",_st($ctx1.locals.set)._printString());
 _st($ctx1.locals.set)._add_("foo");
 _st(self)._assert_equals_("a Set (1 3 'foo')",_st($ctx1.locals.set)._printString());
-_st($ctx1.locals.set)._remove_((1));
-$2=_st($ctx1.locals.set)._remove_((3));
+$3=$ctx1.locals.set;
+_st($3)._remove_((1));
+$4=_st($3)._remove_((3));
 _st(self)._assert_equals_("a Set ('foo')",_st($ctx1.locals.set)._printString());
 _st($ctx1.locals.set)._add_((3));
 _st(self)._assert_equals_("a Set ('foo' 3)",_st($ctx1.locals.set)._printString());
@@ -2716,7 +2795,7 @@ smalltalk.method({
 selector: "testUnicity",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.set=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.set=nil;
 $ctx1.locals.set=_st((smalltalk.Set || Set))._new();
 _st($ctx1.locals.set)._add_((21));
 _st($ctx1.locals.set)._add_("hello");
@@ -2760,25 +2839,29 @@ smalltalk.method({
 selector: "testIfNil",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$5,$4,$6,$7;
+$1=self;
 if(($receiver = nil) == nil || $receiver == undefined){
-$1=true;
+$2=true;
 } else {
-$1=nil;
+$2=nil;
 };
-_st(self)._assert_equals_($1,true);
+_st($1)._assert_equals_($2,true);
+$3=self;
 if(($receiver = nil) == nil || $receiver == undefined){
-$2=nil;
+$5=nil;
 } else {
-$2=true;
+$5=true;
 };
-_st(self)._deny_(_st($2).__eq(true));
+$4=_st($5).__eq(true);
+_st($3)._deny_($4);
+$6=self;
 if(($receiver = nil) == nil || $receiver == undefined){
-$3=true;
+$7=true;
 } else {
-$3=false;
+$7=false;
 };
-_st(self)._assert_equals_($3,true);
+_st($6)._assert_equals_($7,true);
 _st(self)._deny_(_st(_st(nil)._ifNotNil_ifNil_((function(){
 return smalltalk.withContext(function($ctx2) { 
return true;
 })}),(function(){

+ 249 - 156
js/Kernel-Tests.js

@@ -1,5 +1,45 @@
 smalltalk.addPackage('Kernel-Tests', {});
 smalltalk.addClass('BlockClosureTest', smalltalk.TestCase, [], 'Kernel-Tests');
+smalltalk.addMethod(
+"_testCanClearInterval",
+smalltalk.method({
+selector: "testCanClearInterval",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._shouldnt_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(_st((function(){
+return smalltalk.withContext(function($ctx3) { 
return _st(_st((smalltalk.Error || Error))._new())._signal();
+})}))._valueWithInterval_((0)))._clearInterval();
+})}),(smalltalk.Error || Error));
+return self}, self, "testCanClearInterval", [], smalltalk.BlockClosureTest)},
+args: [],
+source: "testCanClearInterval\x0a\x09self shouldnt: [([Error new signal] valueWithInterval: 0) clearInterval] raise: Error",
+messageSends: ["shouldnt:raise:", "clearInterval", "valueWithInterval:", "signal", "new"],
+referencedClasses: ["Error"]
+}),
+smalltalk.BlockClosureTest);
+
+smalltalk.addMethod(
+"_testCanClearTimeout",
+smalltalk.method({
+selector: "testCanClearTimeout",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._shouldnt_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(_st((function(){
+return smalltalk.withContext(function($ctx3) { 
return _st(_st((smalltalk.Error || Error))._new())._signal();
+})}))._valueWithTimeout_((0)))._clearTimeout();
+})}),(smalltalk.Error || Error));
+return self}, self, "testCanClearTimeout", [], smalltalk.BlockClosureTest)},
+args: [],
+source: "testCanClearTimeout\x0a\x09self shouldnt: [([Error new signal] valueWithTimeout: 0) clearTimeout] raise: Error",
+messageSends: ["shouldnt:raise:", "clearTimeout", "valueWithTimeout:", "signal", "new"],
+referencedClasses: ["Error"]
+}),
+smalltalk.BlockClosureTest);
+
 smalltalk.addMethod(
 "_testCompiledSource",
 smalltalk.method({
@@ -157,7 +197,7 @@ selector: "testWhileFalse",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.i=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.i=nil;
 $ctx1.locals.i=(0);
 _st((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st($ctx1.locals.i).__gt((5));
@@ -188,7 +228,7 @@ selector: "testWhileTrue",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.i=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.i=nil;
 $ctx1.locals.i=(0);
 _st((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st($ctx1.locals.i).__lt((5));
@@ -272,47 +312,63 @@ selector: "testIfTrueIfFalse",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4,$5,$6,$7,$8;
+return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2,$4,$6,$5,$7,$9,$8,$10,$12,$11,$13,$15,$14,$16,$18,$17,$19,$21,$20,$22,$24,$23;
+$1=self;
 if(smalltalk.assert(true)){
-$1="alternative block";
+$3="alternative block";
 };
-_st(self)._assert_(_st($1).__eq("alternative block"));
+$2=_st($3).__eq("alternative block");
+_st($1)._assert_($2);
+$4=self;
 if(! smalltalk.assert(true)){
-$2="alternative block";
+$6="alternative block";
 };
-_st(self)._assert_(_st($2).__eq(nil));
+$5=_st($6).__eq(nil);
+_st($4)._assert_($5);
+$7=self;
 if(smalltalk.assert(false)){
-$3="alternative block";
+$9="alternative block";
 };
-_st(self)._assert_(_st($3).__eq(nil));
+$8=_st($9).__eq(nil);
+_st($7)._assert_($8);
+$10=self;
 if(! smalltalk.assert(false)){
-$4="alternative block";
+$12="alternative block";
 };
-_st(self)._assert_(_st($4).__eq("alternative block"));
+$11=_st($12).__eq("alternative block");
+_st($10)._assert_($11);
+$13=self;
 if(smalltalk.assert(false)){
-$5="alternative block";
+$15="alternative block";
 } else {
-$5="alternative block2";
+$15="alternative block2";
 };
-_st(self)._assert_(_st($5).__eq("alternative block2"));
+$14=_st($15).__eq("alternative block2");
+_st($13)._assert_($14);
+$16=self;
 if(smalltalk.assert(false)){
-$6="alternative block2";
+$18="alternative block2";
 } else {
-$6="alternative block";
+$18="alternative block";
 };
-_st(self)._assert_(_st($6).__eq("alternative block"));
+$17=_st($18).__eq("alternative block");
+_st($16)._assert_($17);
+$19=self;
 if(smalltalk.assert(true)){
-$7="alternative block";
+$21="alternative block";
 } else {
-$7="alternative block2";
+$21="alternative block2";
 };
-_st(self)._assert_(_st($7).__eq("alternative block"));
+$20=_st($21).__eq("alternative block");
+_st($19)._assert_($20);
+$22=self;
 if(smalltalk.assert(true)){
-$8="alternative block2";
+$24="alternative block2";
 } else {
-$8="alternative block";
+$24="alternative block";
 };
-_st(self)._assert_(_st($8).__eq("alternative block2"));
+$23=_st($24).__eq("alternative block2");
+_st($22)._assert_($23);
 return self}, self, "testIfTrueIfFalse", [], smalltalk.BooleanTest)},
 args: [],
 source: "testIfTrueIfFalse\x0a \x0a\x09self assert: (true ifTrue: ['alternative block']) = 'alternative block'.\x0a\x09self assert: (true ifFalse: ['alternative block']) = nil.\x0a\x0a\x09self assert: (false ifTrue: ['alternative block']) = nil.\x0a\x09self assert: (false ifFalse: ['alternative block']) = 'alternative block'.\x0a\x0a\x09self assert: (false ifTrue: ['alternative block'] ifFalse: ['alternative block2']) = 'alternative block2'.\x0a\x09self assert: (false ifFalse: ['alternative block'] ifTrue: ['alternative block2']) = 'alternative block'.\x0a\x0a\x09self assert: (true ifTrue: ['alternative block'] ifFalse: ['alternative block2']) = 'alternative block'.\x0a\x09self assert: (true ifFalse: ['alternative block'] ifTrue: ['alternative block2']) = 'alternative block2'.",
@@ -408,21 +464,25 @@ selector: "testLogic",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
-_st(self)._assert_(_st(true).__and(true));
-_st(self)._deny_(_st(true).__and(false));
-_st(self)._deny_(_st(false).__and(true));
-$1=_st(self)._deny_(_st(false).__and(false));
-_st(self)._assert_(_st(true).__or(true));
-_st(self)._assert_(_st(true).__or(false));
-_st(self)._assert_(_st(false).__or(true));
-$2=_st(self)._deny_(_st(false).__or(false));
-_st(self)._assert_(_st(true).__and(_st((1)).__gt((0))));
-_st(self)._deny_(_st(_st((1)).__gt((0))).__and(false));
-$3=_st(self)._deny_(_st(_st((1)).__gt((0))).__and(_st((1)).__gt((2))));
-_st(self)._assert_(_st(false).__or(_st((1)).__gt((0))));
-_st(self)._assert_(_st(_st((1)).__gt((0))).__or(false));
-$4=_st(self)._assert_(_st(_st((1)).__gt((0))).__or(_st((1)).__gt((2))));
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4,$5,$6,$7,$8;
+$1=self;
+_st($1)._assert_(_st(true).__and(true));
+_st($1)._deny_(_st(true).__and(false));
+_st($1)._deny_(_st(false).__and(true));
+$2=_st($1)._deny_(_st(false).__and(false));
+$3=self;
+_st($3)._assert_(_st(true).__or(true));
+_st($3)._assert_(_st(true).__or(false));
+_st($3)._assert_(_st(false).__or(true));
+$4=_st($3)._deny_(_st(false).__or(false));
+$5=self;
+_st($5)._assert_(_st(true).__and(_st((1)).__gt((0))));
+_st($5)._deny_(_st(_st((1)).__gt((0))).__and(false));
+$6=_st($5)._deny_(_st(_st((1)).__gt((0))).__and(_st((1)).__gt((2))));
+$7=self;
+_st($7)._assert_(_st(false).__or(_st((1)).__gt((0))));
+_st($7)._assert_(_st(_st((1)).__gt((0))).__or(false));
+$8=_st($7)._assert_(_st(_st((1)).__gt((0))).__or(_st((1)).__gt((2))));
 return self}, self, "testLogic", [], smalltalk.BooleanTest)},
 args: [],
 source: "testLogic\x0a \x0a\x09\x22Trivial logic table\x22\x0a\x09self assert: (true & true); deny: (true & false); deny: (false & true); deny: (false & false).\x0a\x09self assert: (true | true); assert: (true | false); assert: (false | true); deny: (false | false).\x0a        \x22Checking that expressions work fine too\x22\x0a\x09self assert: (true & (1 > 0)); deny: ((1 > 0) & false); deny: ((1 > 0) & (1 > 2)).\x0a        self assert: (false | (1 > 0)); assert: ((1 > 0) | false); assert: ((1 > 0) | (1 > 2))",
@@ -438,47 +498,51 @@ selector: "testLogicKeywords",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
-_st(self)._assert_(_st(true)._and_((function(){
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4,$5,$6,$7,$8;
+$1=self;
+_st($1)._assert_(_st(true)._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return true;
 })})));
-_st(self)._deny_(_st(true)._and_((function(){
+_st($1)._deny_(_st(true)._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return false;
 })})));
-_st(self)._deny_(_st(false)._and_((function(){
+_st($1)._deny_(_st(false)._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return true;
 })})));
-$1=_st(self)._deny_(_st(false)._and_((function(){
+$2=_st($1)._deny_(_st(false)._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return false;
 })})));
-_st(self)._assert_(_st(true)._or_((function(){
+$3=self;
+_st($3)._assert_(_st(true)._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return true;
 })})));
-_st(self)._assert_(_st(true)._or_((function(){
+_st($3)._assert_(_st(true)._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return false;
 })})));
-_st(self)._assert_(_st(false)._or_((function(){
+_st($3)._assert_(_st(false)._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return true;
 })})));
-$2=_st(self)._deny_(_st(false)._or_((function(){
+$4=_st($3)._deny_(_st(false)._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return false;
 })})));
-_st(self)._assert_(_st(true)._and_((function(){
+$5=self;
+_st($5)._assert_(_st(true)._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st((1)).__gt((0));
 })})));
-_st(self)._deny_(_st(_st((1)).__gt((0)))._and_((function(){
+_st($5)._deny_(_st(_st((1)).__gt((0)))._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return false;
 })})));
-$3=_st(self)._deny_(_st(_st((1)).__gt((0)))._and_((function(){
+$6=_st($5)._deny_(_st(_st((1)).__gt((0)))._and_((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st((1)).__gt((2));
 })})));
-_st(self)._assert_(_st(false)._or_((function(){
+$7=self;
+_st($7)._assert_(_st(false)._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st((1)).__gt((0));
 })})));
-_st(self)._assert_(_st(_st((1)).__gt((0)))._or_((function(){
+_st($7)._assert_(_st(_st((1)).__gt((0)))._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return false;
 })})));
-$4=_st(self)._assert_(_st(_st((1)).__gt((0)))._or_((function(){
+$8=_st($7)._assert_(_st(_st((1)).__gt((0)))._or_((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st((1)).__gt((2));
 })})));
 return self}, self, "testLogicKeywords", [], smalltalk.BooleanTest)},
@@ -538,8 +602,10 @@ selector: "tearDown",
 category: 'running',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
if(($receiver = self["@theClass"]) == nil || $receiver == undefined){
-self["@theClass"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@theClass"];
+if(($receiver = $1) == nil || $receiver == undefined){
+$1;
 } else {
 _st(_st((smalltalk.Smalltalk || Smalltalk))._current())._removeClass_(self["@theClass"]);
 self["@theClass"]=nil;
@@ -737,8 +803,8 @@ selector: "testAsSet",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.c=nil;
-$ctx1.set=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.c=nil;
+$ctx1.locals.set=nil;
 $ctx1.locals.c=_st(self)._collectionWithDuplicates();
 $ctx1.locals.set=_st($ctx1.locals.c)._asSet();
 _st(self)._assert_(_st(_st($ctx1.locals.set)._size()).__eq((5)));
@@ -760,7 +826,7 @@ selector: "testCollect",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection=[(1), (2), (3), (4)];
 _st(self)._assertSameContents_as_(_st(_st(self)._collection())._collect_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st(each)._abs();
@@ -803,7 +869,7 @@ selector: "testDo",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection=_st((smalltalk.OrderedCollection || OrderedCollection))._new();
 _st(_st(self)._collection())._do_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st($ctx1.locals.newCollection)._add_(each);
@@ -841,7 +907,7 @@ selector: "testSelect",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection=[(2), (-4)];
 _st(self)._assertSameContents_as_(_st(_st(self)._collection())._select_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st(each)._even();
@@ -952,7 +1018,9 @@ selector: "collectionClass",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return (smalltalk.HashedCollection || HashedCollection);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=(smalltalk.HashedCollection || HashedCollection);
+return $1;
 }, self, "collectionClass", [], smalltalk.HashedCollectionTest.klass)},
 args: [],
 source: "collectionClass\x0a\x09^ HashedCollection",
@@ -1021,7 +1089,7 @@ selector: "testAccessing",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_("hello","world");
 _st(self)._assert_(_st(_st($ctx1.locals.d)._at_("hello")).__eq("world"));
@@ -1067,8 +1135,8 @@ category: 'tests',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4,$5,$6,$7,$8,$9,$10;
-$ctx1.d1=nil;
-$ctx1.d2=nil;
+$ctx1.locals.d1=nil;
+$ctx1.locals.d2=nil;
 _st(self)._assert_(_st(_st((smalltalk.Dictionary || Dictionary))._new()).__eq(_st((smalltalk.Dictionary || Dictionary))._new()));
 $1=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($1)._at_put_((1),(2));
@@ -1110,8 +1178,8 @@ selector: "testIfAbsent",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
-$ctx1.visited=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
+$ctx1.locals.visited=nil;
 $ctx1.locals.visited=false;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_ifAbsent_("hello",(function(){
@@ -1134,9 +1202,9 @@ selector: "testIfPresent",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
-$ctx1.visited=nil;
-$ctx1.absent=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
+$ctx1.locals.visited=nil;
+$ctx1.locals.absent=nil;
 $ctx1.locals.visited=false;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_("hello","world");
@@ -1152,7 +1220,7 @@ return $ctx1.locals.visited;
 _st(self)._assert_(_st($ctx1.locals.absent)._isNil());
 return self}, self, "testIfPresent", [], smalltalk.DictionaryTest)},
 args: [],
-source: "testIfPresent\x0a\x0a\x09| d visited absent |\x0a\x09visited := false.\x0a\x09d := Dictionary new.\x0a\x09d at: 'hello' put: 'world'.\x0a\x0a\x09d at: 'hello' ifPresent: [ :value | visited := value ].\x0a\x09self assert: visited = 'world'.\x0a\x0a\x09absent := d at: 'bye' ifPresent: [ :value | visited := value ].\x0a\x09self assert: absent isNil.\x0a",
+source: "testIfPresent\x0a\x0a\x09| d visited absent |\x0a\x09visited := false.\x0a\x09d := Dictionary new.\x0a\x09d at: 'hello' put: 'world'.\x0a\x0a\x09d at: 'hello' ifPresent: [ :value | visited := value ].\x0a\x09self assert: visited = 'world'.\x0a\x0a\x09absent := d at: 'bye' ifPresent: [ :value | visited := value ].\x0a\x09self assert: absent isNil.",
 messageSends: ["new", "at:put:", "at:ifPresent:", "assert:", "=", "isNil"],
 referencedClasses: ["Dictionary"]
 }),
@@ -1165,8 +1233,8 @@ selector: "testIfPresentIfAbsent",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
-$ctx1.visited=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
+$ctx1.locals.visited=nil;
 $ctx1.locals.visited=false;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_("hello","world");
@@ -1201,7 +1269,7 @@ selector: "testKeys",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_((1),(2));
 _st($ctx1.locals.d)._at_put_((2),(3));
@@ -1243,8 +1311,8 @@ selector: "testRemoveKey",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
-$ctx1.key=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
+$ctx1.locals.key=nil;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_((1),(2));
 _st($ctx1.locals.d)._at_put_((2),(3));
@@ -1270,8 +1338,8 @@ selector: "testRemoveKeyIfAbsent",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
-$ctx1.key=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
+$ctx1.locals.key=nil;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_((1),(2));
 _st($ctx1.locals.d)._at_put_((2),(3));
@@ -1301,7 +1369,7 @@ selector: "testSize",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st(self)._assert_(_st(_st($ctx1.locals.d)._size()).__eq((0)));
 _st($ctx1.locals.d)._at_put_((1),(2));
@@ -1323,7 +1391,7 @@ selector: "testValues",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.d=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.d=nil;
 $ctx1.locals.d=_st((smalltalk.Dictionary || Dictionary))._new();
 _st($ctx1.locals.d)._at_put_((1),(2));
 _st($ctx1.locals.d)._at_put_((2),(3));
@@ -1345,7 +1413,9 @@ selector: "collectionClass",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return (smalltalk.Dictionary || Dictionary);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=(smalltalk.Dictionary || Dictionary);
+return $1;
 }, self, "collectionClass", [], smalltalk.DictionaryTest.klass)},
 args: [],
 source: "collectionClass\x0a\x09^ Dictionary",
@@ -1403,7 +1473,7 @@ selector: "testAtIfAbsent",
 category: 'testing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.array=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.array=nil;
 $ctx1.locals.array=["hello", "world"];
 _st(self)._assert_equals_(_st($ctx1.locals.array)._at_((1)),"hello");
 _st(self)._assert_equals_(_st($ctx1.locals.array)._at_((2)),"world");
@@ -1468,17 +1538,19 @@ selector: "testPrintString",
 category: 'testing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
-$ctx1.array=nil;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
+$ctx1.locals.array=nil;
 $ctx1.locals.array=_st((smalltalk.Array || Array))._new();
 _st(self)._assert_equals_("a Array ()",_st($ctx1.locals.array)._printString());
-_st($ctx1.locals.array)._add_((1));
-$1=_st($ctx1.locals.array)._add_((3));
+$1=$ctx1.locals.array;
+_st($1)._add_((1));
+$2=_st($1)._add_((3));
 _st(self)._assert_equals_("a Array (1 3)",_st($ctx1.locals.array)._printString());
 _st($ctx1.locals.array)._add_("foo");
 _st(self)._assert_equals_("a Array (1 3 'foo')",_st($ctx1.locals.array)._printString());
-_st($ctx1.locals.array)._remove_((1));
-$2=_st($ctx1.locals.array)._remove_((3));
+$3=$ctx1.locals.array;
+_st($3)._remove_((1));
+$4=_st($3)._remove_((3));
 _st(self)._assert_equals_("a Array ('foo')",_st($ctx1.locals.array)._printString());
 _st($ctx1.locals.array)._addLast_((3));
 _st(self)._assert_equals_("a Array ('foo' 3)",_st($ctx1.locals.array)._printString());
@@ -1500,7 +1572,9 @@ selector: "collectionClass",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return (smalltalk.Array || Array);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=(smalltalk.Array || Array);
+return $1;
 }, self, "collectionClass", [], smalltalk.ArrayTest.klass)},
 args: [],
 source: "collectionClass\x0a\x09^ Array",
@@ -1625,7 +1699,7 @@ selector: "testCollect",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection="hheelllloo";
 _st(self)._assertSameContents_as_(_st(_st(self)._collection())._collect_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st(each).__comma(each);
@@ -1757,7 +1831,7 @@ selector: "testSelect",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection="o";
 _st(self)._assertSameContents_as_(_st(_st(self)._collection())._select_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st(each).__eq("o");
@@ -1794,12 +1868,13 @@ selector: "testStreamContents",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 _st(self)._assert_equals_("hello world",_st((smalltalk.String || String))._streamContents_((function(aStream){
-return smalltalk.withContext(function($ctx2) { 
_st(aStream)._nextPutAll_("hello");
-_st(aStream)._space();
-$1=_st(aStream)._nextPutAll_("world");
-return $1;
+return smalltalk.withContext(function($ctx2) { 
$1=aStream;
+_st($1)._nextPutAll_("hello");
+_st($1)._space();
+$2=_st($1)._nextPutAll_("world");
+return $2;
 })})));
 return self}, self, "testStreamContents", [], smalltalk.StringTest)},
 args: [],
@@ -1817,7 +1892,9 @@ selector: "collectionClass",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return (smalltalk.String || String);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=(smalltalk.String || String);
+return $1;
 }, self, "collectionClass", [], smalltalk.StringTest.klass)},
 args: [],
 source: "collectionClass\x0a\x09^ String",
@@ -1937,7 +2014,7 @@ selector: "testCollect",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection=smalltalk.symbolFor("hheelllloo");
 _st(self)._assertSameContents_as_(_st(_st(self)._collection())._collect_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st(each).__comma(each);
@@ -2096,7 +2173,7 @@ selector: "testSelect",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.newCollection=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.newCollection=nil;
 $ctx1.locals.newCollection="o";
 _st(self)._assertSameContents_as_(_st(_st(self)._collection())._select_((function(each){
 return smalltalk.withContext(function($ctx2) { 
return _st(each).__eq("o");
@@ -2134,7 +2211,9 @@ selector: "collectionClass",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return (smalltalk.Symbol || Symbol);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=(smalltalk.Symbol || Symbol);
+return $1;
 }, self, "collectionClass", [], smalltalk.SymbolTest.klass)},
 args: [],
 source: "collectionClass\x0a\x09^ Symbol",
@@ -2153,7 +2232,6 @@ category: 'accessing',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
return jsObject = {a: 1, b: function() {return 2;}, c: function(object) {return object;}, d: '', 'e': null};
-;
 return self}, self, "jsObject", [], smalltalk.JSObjectProxyTest)},
 args: [],
 source: "jsObject\x0a\x09<return jsObject = {a: 1, b: function() {return 2;}, c: function(object) {return object;}, d: '', 'e': null}>",
@@ -2237,7 +2315,7 @@ selector: "testPropertyThatReturnsEmptyString",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.object=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.object=nil;
 $ctx1.locals.object=_st(self)._jsObject();
 _st(self)._assert_equals_("",_st($ctx1.locals.object)._d());
 _st($ctx1.locals.object)._d_("hello");
@@ -2257,7 +2335,7 @@ selector: "testPropertyThatReturnsUndefined",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.object=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.object=nil;
 $ctx1.locals.object=_st(self)._jsObject();
 _st(self)._shouldnt_raise_((function(){
 return smalltalk.withContext(function($ctx2) { 
return _st($ctx1.locals.object)._e();
@@ -2265,7 +2343,7 @@ return smalltalk.withContext(function($ctx2) { 
return _st($ctx1.locals.object).
 _st(self)._assert_(_st(_st($ctx1.locals.object)._e())._isNil());
 return self}, self, "testPropertyThatReturnsUndefined", [], smalltalk.JSObjectProxyTest)},
 args: [],
-source: "testPropertyThatReturnsUndefined\x0a\x09| object |\x0a\x0a\x09object := self jsObject.\x0a\x09self shouldnt: [ object e ]  raise: MessageNotUnderstood.\x0a    self assert: object e isNil\x0a",
+source: "testPropertyThatReturnsUndefined\x0a\x09| object |\x0a\x0a\x09object := self jsObject.\x0a\x09self shouldnt: [ object e ]  raise: MessageNotUnderstood.\x0a    self assert: object e isNil",
 messageSends: ["jsObject", "shouldnt:raise:", "e", "assert:", "isNil"],
 referencedClasses: ["MessageNotUnderstood"]
 }),
@@ -2279,7 +2357,7 @@ category: 'tests',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
var $1,$2;
-$ctx1.object=nil;
+$ctx1.locals.object=nil;
 $1=_st(self)._jsObject();
 _st($1)._d_("test");
 $2=_st($1)._yourself();
@@ -2702,7 +2780,7 @@ selector: "testTimesRepeat",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.i=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.i=nil;
 $ctx1.locals.i=(0);
 _st((0))._timesRepeat_((function(){
 return smalltalk.withContext(function($ctx2) { 
$ctx1.locals.i=_st($ctx1.locals.i).__plus((1));
@@ -2785,7 +2863,9 @@ selector: "foo",
 category: 'not yet classified',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@foo"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@foo"];
+return $1;
 }, self, "foo", [], smalltalk.ObjectMock)},
 args: [],
 source: "foo\x0a\x09^foo",
@@ -2821,7 +2901,6 @@ category: 'tests',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
return undefined;;
-;
 return self}, self, "notDefined", [], smalltalk.ObjectTest)},
 args: [],
 source: "notDefined\x0a\x09<return undefined;>",
@@ -2837,7 +2916,7 @@ selector: "testBasicAccess",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o=nil;
 $ctx1.locals.o=_st((smalltalk.Object || Object))._new();
 _st($ctx1.locals.o)._basicAt_put_("a",(1));
 _st(self)._assert_equals_(_st($ctx1.locals.o)._basicAt_("a"),(1));
@@ -2857,7 +2936,7 @@ selector: "testBasicPerform",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o=nil;
 $ctx1.locals.o=_st((smalltalk.Object || Object))._new();
 _st($ctx1.locals.o)._basicAt_put_("func",(function(){
 return smalltalk.withContext(function($ctx2) { 
return "hello";
@@ -2900,7 +2979,7 @@ selector: "testEquality",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o=nil;
 $ctx1.locals.o=_st((smalltalk.Object || Object))._new();
 _st(self)._deny_(_st($ctx1.locals.o).__eq(_st((smalltalk.Object || Object))._new()));
 _st(self)._assert_(_st($ctx1.locals.o).__eq($ctx1.locals.o));
@@ -2939,7 +3018,7 @@ selector: "testIdentity",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o=nil;
 $ctx1.locals.o=_st((smalltalk.Object || Object))._new();
 _st(self)._deny_(_st($ctx1.locals.o).__eq_eq(_st((smalltalk.Object || Object))._new()));
 _st(self)._assert_(_st($ctx1.locals.o).__eq_eq($ctx1.locals.o));
@@ -2960,29 +3039,35 @@ selector: "testIfNil",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$1,$4,$3,$6,$5;
+return smalltalk.withContext(function($ctx1) { 
var $1,$4,$3,$2,$5,$8,$7,$6,$9,$12,$11,$10;
 _st(self)._deny_(_st(_st((smalltalk.Object || Object))._new())._isNil());
-$2=_st((smalltalk.Object || Object))._new();
-if(($receiver = $2) == nil || $receiver == undefined){
-$1=true;
-} else {
-$1=$2;
-};
-_st(self)._deny_(_st($1).__eq(true));
+$1=self;
 $4=_st((smalltalk.Object || Object))._new();
 if(($receiver = $4) == nil || $receiver == undefined){
+$3=true;
+} else {
 $3=$4;
+};
+$2=_st($3).__eq(true);
+_st($1)._deny_($2);
+$5=self;
+$8=_st((smalltalk.Object || Object))._new();
+if(($receiver = $8) == nil || $receiver == undefined){
+$7=$8;
 } else {
-$3=true;
+$7=true;
 };
-_st(self)._assert_(_st($3).__eq(true));
-$6=_st((smalltalk.Object || Object))._new();
-if(($receiver = $6) == nil || $receiver == undefined){
-$5=false;
+$6=_st($7).__eq(true);
+_st($5)._assert_($6);
+$9=self;
+$12=_st((smalltalk.Object || Object))._new();
+if(($receiver = $12) == nil || $receiver == undefined){
+$11=false;
 } else {
-$5=true;
+$11=true;
 };
-_st(self)._assert_(_st($5).__eq(true));
+$10=_st($11).__eq(true);
+_st($9)._assert_($10);
 _st(self)._assert_(_st(_st(_st((smalltalk.Object || Object))._new())._ifNotNil_ifNil_((function(){
 return smalltalk.withContext(function($ctx2) { 
return true;
 })}),(function(){
@@ -3003,7 +3088,7 @@ selector: "testInstVars",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o=nil;
 $ctx1.locals.o=_st((smalltalk.ObjectMock || ObjectMock))._new();
 _st(self)._assert_equals_(_st($ctx1.locals.o)._instVarAt_(smalltalk.symbolFor("foo")),nil);
 _st($ctx1.locals.o)._instVarAt_put_(smalltalk.symbolFor("foo"),(1));
@@ -3040,7 +3125,7 @@ selector: "testYourself",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o=nil;
 $ctx1.locals.o=_st((smalltalk.ObjectMock || ObjectMock))._new();
 _st(self)._assert_(_st(_st($ctx1.locals.o)._yourself()).__eq_eq($ctx1.locals.o));
 return self}, self, "testYourself", [], smalltalk.ObjectTest)},
@@ -3058,8 +3143,8 @@ selector: "testidentityHash",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.o1=nil;
-$ctx1.o2=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.o1=nil;
+$ctx1.locals.o2=nil;
 $ctx1.locals.o1=_st((smalltalk.Object || Object))._new();
 $ctx1.locals.o2=_st((smalltalk.Object || Object))._new();
 _st(self)._assert_(_st(_st($ctx1.locals.o1)._identityHash()).__eq_eq(_st($ctx1.locals.o1)._identityHash()));
@@ -3108,9 +3193,10 @@ selector: "tearDown",
 category: 'running',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-_st((smalltalk.Package || Package))._defaultCommitPathJs_(self["@backUpCommitPathJs"]);
-$1=_st((smalltalk.Package || Package))._defaultCommitPathSt_(self["@backUpCommitPathSt"]);
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+$1=(smalltalk.Package || Package);
+_st($1)._defaultCommitPathJs_(self["@backUpCommitPathJs"]);
+$2=_st($1)._defaultCommitPathSt_(self["@backUpCommitPathSt"]);
 return self}, self, "tearDown", [], smalltalk.PackageTest)},
 args: [],
 source: "tearDown\x0a\x09 Package \x0a\x09\x09defaultCommitPathJs: backUpCommitPathJs;\x0a\x09\x09defaultCommitPathSt: backUpCommitPathSt",
@@ -3193,10 +3279,11 @@ selector: "setUp",
 category: 'running',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
 smalltalk.PackageTest.fn.prototype._setUp.apply(_st(self), []);
-_st((smalltalk.Package || Package))._defaultCommitPathJs_("javascripts/");
-$1=_st((smalltalk.Package || Package))._defaultCommitPathSt_("smalltalk/");
+$1=(smalltalk.Package || Package);
+_st($1)._defaultCommitPathJs_("javascripts/");
+$2=_st($1)._defaultCommitPathSt_("smalltalk/");
 return self}, self, "setUp", [], smalltalk.PackageWithDefaultCommitPathChangedTest)},
 args: [],
 source: "setUp\x0a\x09super setUp.\x0a\x0a\x09Package\x0a\x09\x09defaultCommitPathJs: 'javascripts/';\x0a\x09\x09defaultCommitPathSt: 'smalltalk/'.",
@@ -3389,8 +3476,8 @@ category: 'tests',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
_st((10000))._timesRepeat_((function(){
-return smalltalk.withContext(function($ctx2) { 
$ctx2.current=nil;
-$ctx2.next=nil;
+return smalltalk.withContext(function($ctx2) { 
$ctx2.locals.current=nil;
+$ctx2.locals.next=nil;
 $ctx2.locals.next=_st(_st((smalltalk.Random || Random))._new())._next();
 $ctx2.locals.next;
 _st(self)._assert_(_st($ctx2.locals.next).__gt_eq((0)));
@@ -3416,7 +3503,7 @@ selector: "testAddRemove",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.set=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.set=nil;
 $ctx1.locals.set=_st((smalltalk.Set || Set))._new();
 _st(self)._assert_(_st($ctx1.locals.set)._isEmpty());
 _st($ctx1.locals.set)._add_((3));
@@ -3458,17 +3545,19 @@ selector: "testPrintString",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2;
-$ctx1.set=nil;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$4;
+$ctx1.locals.set=nil;
 $ctx1.locals.set=_st((smalltalk.Set || Set))._new();
 _st(self)._assert_equals_("a Set ()",_st($ctx1.locals.set)._printString());
-_st($ctx1.locals.set)._add_((1));
-$1=_st($ctx1.locals.set)._add_((3));
+$1=$ctx1.locals.set;
+_st($1)._add_((1));
+$2=_st($1)._add_((3));
 _st(self)._assert_equals_("a Set (1 3)",_st($ctx1.locals.set)._printString());
 _st($ctx1.locals.set)._add_("foo");
 _st(self)._assert_equals_("a Set (1 3 'foo')",_st($ctx1.locals.set)._printString());
-_st($ctx1.locals.set)._remove_((1));
-$2=_st($ctx1.locals.set)._remove_((3));
+$3=$ctx1.locals.set;
+_st($3)._remove_((1));
+$4=_st($3)._remove_((3));
 _st(self)._assert_equals_("a Set ('foo')",_st($ctx1.locals.set)._printString());
 _st($ctx1.locals.set)._add_((3));
 _st(self)._assert_equals_("a Set ('foo' 3)",_st($ctx1.locals.set)._printString());
@@ -3507,7 +3596,7 @@ selector: "testUnicity",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.set=nil;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.set=nil;
 $ctx1.locals.set=_st((smalltalk.Set || Set))._new();
 _st($ctx1.locals.set)._add_((21));
 _st($ctx1.locals.set)._add_("hello");
@@ -3566,25 +3655,29 @@ selector: "testIfNil",
 category: 'tests',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$3,$5,$4,$6,$7;
+$1=self;
 if(($receiver = nil) == nil || $receiver == undefined){
-$1=true;
+$2=true;
 } else {
-$1=nil;
+$2=nil;
 };
-_st(self)._assert_equals_($1,true);
+_st($1)._assert_equals_($2,true);
+$3=self;
 if(($receiver = nil) == nil || $receiver == undefined){
-$2=nil;
+$5=nil;
 } else {
-$2=true;
+$5=true;
 };
-_st(self)._deny_(_st($2).__eq(true));
+$4=_st($5).__eq(true);
+_st($3)._deny_($4);
+$6=self;
 if(($receiver = nil) == nil || $receiver == undefined){
-$3=true;
+$7=true;
 } else {
-$3=false;
+$7=false;
 };
-_st(self)._assert_equals_($3,true);
+_st($6)._assert_equals_($7,true);
 _st(self)._deny_(_st(_st(nil)._ifNotNil_ifNil_((function(){
 return smalltalk.withContext(function($ctx2) { 
return true;
 })}),(function(){

+ 408 - 0
js/SUnit-Tests.deploy.js

@@ -0,0 +1,408 @@
+smalltalk.addPackage('SUnit-Tests', {});
+smalltalk.addClass('ExampleSetTest', smalltalk.TestCase, ['empty', 'full'], 'SUnit-Tests');
+smalltalk.addMethod(
+"_setUp",
+smalltalk.method({
+selector: "setUp",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@empty"]=_st((smalltalk.Set || Set))._new();
+self["@full"]=_st((smalltalk.Set || Set))._with_with_((5),smalltalk.symbolFor("abc"));
+return self}, self, "setUp", [], smalltalk.ExampleSetTest)}
+}),
+smalltalk.ExampleSetTest);
+
+smalltalk.addMethod(
+"_testAdd",
+smalltalk.method({
+selector: "testAdd",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self["@empty"])._add_((5));
+_st(self)._assert_(_st(self["@empty"])._includes_((5)));
+return self}, self, "testAdd", [], smalltalk.ExampleSetTest)}
+}),
+smalltalk.ExampleSetTest);
+
+smalltalk.addMethod(
+"_testGrow",
+smalltalk.method({
+selector: "testGrow",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self["@empty"])._addAll_(_st((1))._to_((100)));
+_st(self)._assert_(_st(_st(self["@empty"])._size()).__eq((100)));
+return self}, self, "testGrow", [], smalltalk.ExampleSetTest)}
+}),
+smalltalk.ExampleSetTest);
+
+smalltalk.addMethod(
+"_testIllegal",
+smalltalk.method({
+selector: "testIllegal",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._should_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self["@empty"])._at_((5));
+})}),(smalltalk.Error || Error));
+_st(self)._should_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self["@empty"])._at_put_((5),smalltalk.symbolFor("abc"));
+})}),(smalltalk.Error || Error));
+return self}, self, "testIllegal", [], smalltalk.ExampleSetTest)}
+}),
+smalltalk.ExampleSetTest);
+
+smalltalk.addMethod(
+"_testIncludes",
+smalltalk.method({
+selector: "testIncludes",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_(_st(self["@full"])._includes_((5)));
+_st(self)._assert_(_st(self["@full"])._includes_(smalltalk.symbolFor("abc")));
+return self}, self, "testIncludes", [], smalltalk.ExampleSetTest)}
+}),
+smalltalk.ExampleSetTest);
+
+smalltalk.addMethod(
+"_testOccurrences",
+smalltalk.method({
+selector: "testOccurrences",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_(_st(_st(self["@empty"])._occurrencesOf_((0))).__eq((0)));
+_st(self)._assert_(_st(_st(self["@full"])._occurrencesOf_((5))).__eq((1)));
+_st(self["@full"])._add_((5));
+_st(self)._assert_(_st(_st(self["@full"])._occurrencesOf_((5))).__eq((1)));
+return self}, self, "testOccurrences", [], smalltalk.ExampleSetTest)}
+}),
+smalltalk.ExampleSetTest);
+
+smalltalk.addMethod(
+"_testRemove",
+smalltalk.method({
+selector: "testRemove",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self["@full"])._remove_((5));
+_st(self)._assert_(_st(self["@full"])._includes_(smalltalk.symbolFor("abc")));
+_st(self)._deny_(_st(self["@full"])._includes_((5)));
+return self}, self, "testRemove", [], smalltalk.ExampleSetTest)}
+}),
+smalltalk.ExampleSetTest);
+
+
+
+smalltalk.addClass('SUnitAsyncTest', smalltalk.TestCase, ['flag'], 'SUnit-Tests');
+smalltalk.addMethod(
+"_fakeError",
+smalltalk.method({
+selector: "fakeError",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@flag"]="bad";
+_st(self)._timeout_((10));
+self["@flag"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
self["@flag"]="ok";
+self["@flag"];
+return _st(self)._error_("Intentional");
+})})))._valueWithTimeout_((5));
+return self}, self, "fakeError", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_fakeErrorFailingInTearDown",
+smalltalk.method({
+selector: "fakeErrorFailingInTearDown",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@flag"]="bad";
+_st(self)._timeout_((10));
+self["@flag"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._error_("Intentional");
+})})))._valueWithTimeout_((5));
+return self}, self, "fakeErrorFailingInTearDown", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_fakeFailure",
+smalltalk.method({
+selector: "fakeFailure",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@flag"]="bad";
+_st(self)._timeout_((10));
+self["@flag"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
self["@flag"]="ok";
+self["@flag"];
+return _st(self)._assert_(false);
+})})))._valueWithTimeout_((5));
+return self}, self, "fakeFailure", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_fakeMultipleTimeoutFailing",
+smalltalk.method({
+selector: "fakeMultipleTimeoutFailing",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._timeout_((100));
+_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._timeout_((5));
+return _st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx3) { 
return _st(self)._finished();
+})})))._valueWithTimeout_((10));
+})})))._valueWithTimeout_((5));
+return self}, self, "fakeMultipleTimeoutFailing", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_fakeMultipleTimeoutPassing",
+smalltalk.method({
+selector: "fakeMultipleTimeoutPassing",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._timeout_((10));
+_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._timeout_((20));
+return _st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx3) { 
return _st(self)._finished();
+})})))._valueWithTimeout_((10));
+})})))._valueWithTimeout_((5));
+return self}, self, "fakeMultipleTimeoutPassing", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_fakeTimeout",
+smalltalk.method({
+selector: "fakeTimeout",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._timeout_((4));
+_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._finished();
+})})))._valueWithTimeout_((5));
+return self}, self, "fakeTimeout", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_setUp",
+smalltalk.method({
+selector: "setUp",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@flag"]="ok";
+return self}, self, "setUp", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_sortedSelectors_",
+smalltalk.method({
+selector: "sortedSelectors:",
+fn: function (aCollection){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(_st(aCollection)._collect_((function(each){
+return smalltalk.withContext(function($ctx2) { 
return _st(each)._selector();
+})})))._sorted();
+return $1;
+}, self, "sortedSelectors:", [aCollection], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_tearDown",
+smalltalk.method({
+selector: "tearDown",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_("ok",self["@flag"]);
+return self}, self, "tearDown", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testAsyncErrorsAndFailures",
+smalltalk.method({
+selector: "testAsyncErrorsAndFailures",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$6,$5,$3;
+$ctx1.locals.suite=nil;
+$ctx1.locals.runner=nil;
+$ctx1.locals.result=nil;
+$ctx1.locals.assertBlock=nil;
+$ctx1.locals.suite=_st(["fakeError", "fakeErrorFailingInTearDown", "fakeFailure", "testPass"])._collect_((function(each){
+return smalltalk.withContext(function($ctx2) { 
return _st(_st(self)._class())._selector_(each);
+})}));
+$ctx1.locals.runner=_st((smalltalk.TestSuiteRunner || TestSuiteRunner))._on_($ctx1.locals.suite);
+_st(self)._timeout_((200));
+$ctx1.locals.result=_st($ctx1.locals.runner)._result();
+$ctx1.locals.assertBlock=_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._assert_equals_(["fakeError"],_st(self)._sortedSelectors_(_st($ctx1.locals.result)._errors()));
+_st(self)._assert_equals_(["fakeErrorFailingInTearDown", "fakeFailure"],_st(self)._sortedSelectors_(_st($ctx1.locals.result)._failures()));
+return _st(self)._finished();
+})}));
+$1=_st($ctx1.locals.runner)._announcer();
+$2=(smalltalk.ResultAnnouncement || ResultAnnouncement);
+$3=(function(ann){
+return smalltalk.withContext(function($ctx2) { 
$4=_st(_st(ann)._result()).__eq_eq($ctx1.locals.result);
+$5=(function(){
+return smalltalk.withContext(function($ctx3) { 
$6=_st(_st($ctx1.locals.result)._runs()).__eq(_st($ctx1.locals.result)._total());
+return _st($6)._ifTrue_($ctx1.locals.assertBlock);
+})});
+return _st($4)._ifTrue_($5);
+})});
+_st($1)._on_do_($2,$3);
+_st($ctx1.locals.runner)._run();
+return self}, self, "testAsyncErrorsAndFailures", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testAsyncNeedsTimeout",
+smalltalk.method({
+selector: "testAsyncNeedsTimeout",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._should_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._async_((function(){
+return smalltalk.withContext(function($ctx3) { 
})}));
+})}),(smalltalk.Error || Error));
+_st(self)._timeout_((0));
+_st(self)._shouldnt_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._async_((function(){
+return smalltalk.withContext(function($ctx3) { 
})}));
+})}),(smalltalk.Error || Error));
+_st(self)._finished();
+return self}, self, "testAsyncNeedsTimeout", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testFinishedNeedsTimeout",
+smalltalk.method({
+selector: "testFinishedNeedsTimeout",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._should_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._finished();
+})}),(smalltalk.Error || Error));
+_st(self)._timeout_((0));
+_st(self)._shouldnt_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._finished();
+})}),(smalltalk.Error || Error));
+return self}, self, "testFinishedNeedsTimeout", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testIsAsyncReturnsCorrectValues",
+smalltalk.method({
+selector: "testIsAsyncReturnsCorrectValues",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._deny_(_st(self)._isAsync());
+_st(self)._timeout_((0));
+_st(self)._assert_(_st(self)._isAsync());
+_st(self)._finished();
+_st(self)._deny_(_st(self)._isAsync());
+return self}, self, "testIsAsyncReturnsCorrectValues", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testPass",
+smalltalk.method({
+selector: "testPass",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@flag"]="bad";
+_st(self)._timeout_((10));
+self["@flag"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._assert_(true);
+_st(self)._finished();
+self["@flag"]="ok";
+return self["@flag"];
+})})))._valueWithTimeout_((5));
+return self}, self, "testPass", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testTimeouts",
+smalltalk.method({
+selector: "testTimeouts",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$6,$5,$3;
+$ctx1.locals.suite=nil;
+$ctx1.locals.runner=nil;
+$ctx1.locals.result=nil;
+$ctx1.locals.assertBlock=nil;
+$ctx1.locals.suite=_st(["fakeTimeout", "fakeMultipleTimeoutFailing", "fakeMultipleTimeoutPassing", "testPass"])._collect_((function(each){
+return smalltalk.withContext(function($ctx2) { 
return _st(_st(self)._class())._selector_(each);
+})}));
+$ctx1.locals.runner=_st((smalltalk.TestSuiteRunner || TestSuiteRunner))._on_($ctx1.locals.suite);
+_st(self)._timeout_((200));
+$ctx1.locals.result=_st($ctx1.locals.runner)._result();
+$ctx1.locals.assertBlock=_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._assert_(_st(_st($ctx1.locals.result)._errors())._isEmpty());
+_st(self)._assert_equals_(["fakeMultipleTimeoutFailing", "fakeTimeout"],_st(self)._sortedSelectors_(_st($ctx1.locals.result)._failures()));
+return _st(self)._finished();
+})}));
+$1=_st($ctx1.locals.runner)._announcer();
+$2=(smalltalk.ResultAnnouncement || ResultAnnouncement);
+$3=(function(ann){
+return smalltalk.withContext(function($ctx2) { 
$4=_st(_st(ann)._result()).__eq_eq($ctx1.locals.result);
+$5=(function(){
+return smalltalk.withContext(function($ctx3) { 
$6=_st(_st($ctx1.locals.result)._runs()).__eq(_st($ctx1.locals.result)._total());
+return _st($6)._ifTrue_($ctx1.locals.assertBlock);
+})});
+return _st($4)._ifTrue_($5);
+})});
+_st($1)._on_do_($2,$3);
+_st($ctx1.locals.runner)._run();
+return self}, self, "testTimeouts", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testTwoAsyncPassesWithFinishedOnlyOneIsRun",
+smalltalk.method({
+selector: "testTwoAsyncPassesWithFinishedOnlyOneIsRun",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.x=nil;
+self["@flag"]="bad";
+_st(self)._timeout_((10));
+$ctx1.locals.x=(0);
+self["@flag"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._finished();
+self["@flag"]="ok";
+self["@flag"];
+$ctx1.locals.x=_st($ctx1.locals.x).__plus((1));
+$ctx1.locals.x;
+return _st(self)._assert_equals_((1),$ctx1.locals.x);
+})})))._valueWithTimeout_((0));
+self["@flag"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._finished();
+self["@flag"]="ok";
+self["@flag"];
+$ctx1.locals.x=_st($ctx1.locals.x).__plus((1));
+$ctx1.locals.x;
+return _st(self)._assert_equals_((1),$ctx1.locals.x);
+})})))._valueWithTimeout_((0));
+return self}, self, "testTwoAsyncPassesWithFinishedOnlyOneIsRun", [], smalltalk.SUnitAsyncTest)}
+}),
+smalltalk.SUnitAsyncTest);
+
+
+

+ 524 - 0
js/SUnit-Tests.js

@@ -0,0 +1,524 @@
+smalltalk.addPackage('SUnit-Tests', {});
+smalltalk.addClass('ExampleSetTest', smalltalk.TestCase, ['empty', 'full'], 'SUnit-Tests');
+smalltalk.ExampleSetTest.comment="ExampleSetTest is taken from Pharo 1.4.\x0a\x0aTHe purpose of this class is to demonstrate a simple use case of the test framework."
+smalltalk.addMethod(
+"_setUp",
+smalltalk.method({
+selector: "setUp",
+category: 'running',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@empty"]=_st((smalltalk.Set || Set))._new();
+self["@full"]=_st((smalltalk.Set || Set))._with_with_((5),smalltalk.symbolFor("abc"));
+return self}, self, "setUp", [], smalltalk.ExampleSetTest)},
+args: [],
+source: "setUp\x0a\x09empty := Set new.\x0a\x09full := Set with: 5 with: #abc",
+messageSends: ["new", "with:with:"],
+referencedClasses: ["Set"]
+}),
+smalltalk.ExampleSetTest);
+
+smalltalk.addMethod(
+"_testAdd",
+smalltalk.method({
+selector: "testAdd",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self["@empty"])._add_((5));
+_st(self)._assert_(_st(self["@empty"])._includes_((5)));
+return self}, self, "testAdd", [], smalltalk.ExampleSetTest)},
+args: [],
+source: "testAdd\x0a\x09empty add: 5.\x0a\x09self assert: (empty includes: 5)",
+messageSends: ["add:", "assert:", "includes:"],
+referencedClasses: []
+}),
+smalltalk.ExampleSetTest);
+
+smalltalk.addMethod(
+"_testGrow",
+smalltalk.method({
+selector: "testGrow",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self["@empty"])._addAll_(_st((1))._to_((100)));
+_st(self)._assert_(_st(_st(self["@empty"])._size()).__eq((100)));
+return self}, self, "testGrow", [], smalltalk.ExampleSetTest)},
+args: [],
+source: "testGrow\x0a\x09empty addAll: (1 to: 100).\x0a\x09self assert: empty size = 100",
+messageSends: ["addAll:", "to:", "assert:", "=", "size"],
+referencedClasses: []
+}),
+smalltalk.ExampleSetTest);
+
+smalltalk.addMethod(
+"_testIllegal",
+smalltalk.method({
+selector: "testIllegal",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._should_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self["@empty"])._at_((5));
+})}),(smalltalk.Error || Error));
+_st(self)._should_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self["@empty"])._at_put_((5),smalltalk.symbolFor("abc"));
+})}),(smalltalk.Error || Error));
+return self}, self, "testIllegal", [], smalltalk.ExampleSetTest)},
+args: [],
+source: "testIllegal\x0a\x09self \x0a\x09\x09should: [empty at: 5] \x0a\x09\x09raise: Error.\x0a\x09self \x0a\x09\x09should: [empty at: 5 put: #abc] \x0a\x09\x09raise: Error",
+messageSends: ["should:raise:", "at:", "at:put:"],
+referencedClasses: ["Error"]
+}),
+smalltalk.ExampleSetTest);
+
+smalltalk.addMethod(
+"_testIncludes",
+smalltalk.method({
+selector: "testIncludes",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_(_st(self["@full"])._includes_((5)));
+_st(self)._assert_(_st(self["@full"])._includes_(smalltalk.symbolFor("abc")));
+return self}, self, "testIncludes", [], smalltalk.ExampleSetTest)},
+args: [],
+source: "testIncludes\x0a\x09self assert: (full includes: 5).\x0a\x09self assert: (full includes: #abc)",
+messageSends: ["assert:", "includes:"],
+referencedClasses: []
+}),
+smalltalk.ExampleSetTest);
+
+smalltalk.addMethod(
+"_testOccurrences",
+smalltalk.method({
+selector: "testOccurrences",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_(_st(_st(self["@empty"])._occurrencesOf_((0))).__eq((0)));
+_st(self)._assert_(_st(_st(self["@full"])._occurrencesOf_((5))).__eq((1)));
+_st(self["@full"])._add_((5));
+_st(self)._assert_(_st(_st(self["@full"])._occurrencesOf_((5))).__eq((1)));
+return self}, self, "testOccurrences", [], smalltalk.ExampleSetTest)},
+args: [],
+source: "testOccurrences\x0a\x09self assert: (empty occurrencesOf: 0) = 0.\x0a\x09self assert: (full occurrencesOf: 5) = 1.\x0a\x09full add: 5.\x0a\x09self assert: (full occurrencesOf: 5) = 1",
+messageSends: ["assert:", "=", "occurrencesOf:", "add:"],
+referencedClasses: []
+}),
+smalltalk.ExampleSetTest);
+
+smalltalk.addMethod(
+"_testRemove",
+smalltalk.method({
+selector: "testRemove",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self["@full"])._remove_((5));
+_st(self)._assert_(_st(self["@full"])._includes_(smalltalk.symbolFor("abc")));
+_st(self)._deny_(_st(self["@full"])._includes_((5)));
+return self}, self, "testRemove", [], smalltalk.ExampleSetTest)},
+args: [],
+source: "testRemove\x0a\x09full remove: 5.\x0a\x09self assert: (full includes: #abc).\x0a\x09self deny: (full includes: 5)",
+messageSends: ["remove:", "assert:", "includes:", "deny:"],
+referencedClasses: []
+}),
+smalltalk.ExampleSetTest);
+
+
+
+smalltalk.addClass('SUnitAsyncTest', smalltalk.TestCase, ['flag'], 'SUnit-Tests');
+smalltalk.addMethod(
+"_fakeError",
+smalltalk.method({
+selector: "fakeError",
+category: 'helpers',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@flag"]="bad";
+_st(self)._timeout_((10));
+self["@flag"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
self["@flag"]="ok";
+self["@flag"];
+return _st(self)._error_("Intentional");
+})})))._valueWithTimeout_((5));
+return self}, self, "fakeError", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "fakeError\x0a\x09flag := 'bad'.\x0a\x09self timeout: 10.\x0a    flag := (self async: [ flag := 'ok'. self error: 'Intentional' ]) valueWithTimeout: 5",
+messageSends: ["timeout:", "valueWithTimeout:", "async:", "error:"],
+referencedClasses: []
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_fakeErrorFailingInTearDown",
+smalltalk.method({
+selector: "fakeErrorFailingInTearDown",
+category: 'helpers',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@flag"]="bad";
+_st(self)._timeout_((10));
+self["@flag"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._error_("Intentional");
+})})))._valueWithTimeout_((5));
+return self}, self, "fakeErrorFailingInTearDown", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "fakeErrorFailingInTearDown\x0a\x09flag := 'bad'.\x0a\x09self timeout: 10.\x0a    flag := (self async: [ self error: 'Intentional' ]) valueWithTimeout: 5",
+messageSends: ["timeout:", "valueWithTimeout:", "async:", "error:"],
+referencedClasses: []
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_fakeFailure",
+smalltalk.method({
+selector: "fakeFailure",
+category: 'helpers',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@flag"]="bad";
+_st(self)._timeout_((10));
+self["@flag"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
self["@flag"]="ok";
+self["@flag"];
+return _st(self)._assert_(false);
+})})))._valueWithTimeout_((5));
+return self}, self, "fakeFailure", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "fakeFailure\x0a\x09flag := 'bad'.\x0a\x09self timeout: 10.\x0a    flag := (self async: [ flag := 'ok'. self assert: false ]) valueWithTimeout: 5",
+messageSends: ["timeout:", "valueWithTimeout:", "async:", "assert:"],
+referencedClasses: []
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_fakeMultipleTimeoutFailing",
+smalltalk.method({
+selector: "fakeMultipleTimeoutFailing",
+category: 'helpers',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._timeout_((100));
+_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._timeout_((5));
+return _st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx3) { 
return _st(self)._finished();
+})})))._valueWithTimeout_((10));
+})})))._valueWithTimeout_((5));
+return self}, self, "fakeMultipleTimeoutFailing", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "fakeMultipleTimeoutFailing\x0a\x09self timeout: 100.\x0a    (self async: [\x0a\x09\x09self timeout: 5.\x0a        (self async: [ self finished ]) valueWithTimeout: 10\x0a\x09]) valueWithTimeout: 5",
+messageSends: ["timeout:", "valueWithTimeout:", "async:", "finished"],
+referencedClasses: []
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_fakeMultipleTimeoutPassing",
+smalltalk.method({
+selector: "fakeMultipleTimeoutPassing",
+category: 'helpers',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._timeout_((10));
+_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._timeout_((20));
+return _st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx3) { 
return _st(self)._finished();
+})})))._valueWithTimeout_((10));
+})})))._valueWithTimeout_((5));
+return self}, self, "fakeMultipleTimeoutPassing", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "fakeMultipleTimeoutPassing\x0a\x09self timeout: 10.\x0a    (self async: [\x0a\x09\x09self timeout: 20.\x0a        (self async: [ self finished ]) valueWithTimeout: 10\x0a\x09]) valueWithTimeout: 5",
+messageSends: ["timeout:", "valueWithTimeout:", "async:", "finished"],
+referencedClasses: []
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_fakeTimeout",
+smalltalk.method({
+selector: "fakeTimeout",
+category: 'helpers',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._timeout_((4));
+_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._finished();
+})})))._valueWithTimeout_((5));
+return self}, self, "fakeTimeout", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "fakeTimeout\x0a\x09self timeout: 4.\x0a    (self async: [ self finished ]) valueWithTimeout: 5",
+messageSends: ["timeout:", "valueWithTimeout:", "async:", "finished"],
+referencedClasses: []
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_setUp",
+smalltalk.method({
+selector: "setUp",
+category: 'running',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@flag"]="ok";
+return self}, self, "setUp", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "setUp\x0a\x09flag := 'ok'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_sortedSelectors_",
+smalltalk.method({
+selector: "sortedSelectors:",
+category: 'private',
+fn: function (aCollection){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(_st(aCollection)._collect_((function(each){
+return smalltalk.withContext(function($ctx2) { 
return _st(each)._selector();
+})})))._sorted();
+return $1;
+}, self, "sortedSelectors:", [aCollection], smalltalk.SUnitAsyncTest)},
+args: ["aCollection"],
+source: "sortedSelectors: aCollection\x0a\x09^(aCollection collect: [:each | each selector]) sorted",
+messageSends: ["sorted", "collect:", "selector"],
+referencedClasses: []
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_tearDown",
+smalltalk.method({
+selector: "tearDown",
+category: 'running',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._assert_equals_("ok",self["@flag"]);
+return self}, self, "tearDown", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "tearDown\x0a\x09self assert: 'ok' equals: flag",
+messageSends: ["assert:equals:"],
+referencedClasses: []
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testAsyncErrorsAndFailures",
+smalltalk.method({
+selector: "testAsyncErrorsAndFailures",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$6,$5,$3;
+$ctx1.locals.suite=nil;
+$ctx1.locals.runner=nil;
+$ctx1.locals.result=nil;
+$ctx1.locals.assertBlock=nil;
+$ctx1.locals.suite=_st(["fakeError", "fakeErrorFailingInTearDown", "fakeFailure", "testPass"])._collect_((function(each){
+return smalltalk.withContext(function($ctx2) { 
return _st(_st(self)._class())._selector_(each);
+})}));
+$ctx1.locals.runner=_st((smalltalk.TestSuiteRunner || TestSuiteRunner))._on_($ctx1.locals.suite);
+_st(self)._timeout_((200));
+$ctx1.locals.result=_st($ctx1.locals.runner)._result();
+$ctx1.locals.assertBlock=_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._assert_equals_(["fakeError"],_st(self)._sortedSelectors_(_st($ctx1.locals.result)._errors()));
+_st(self)._assert_equals_(["fakeErrorFailingInTearDown", "fakeFailure"],_st(self)._sortedSelectors_(_st($ctx1.locals.result)._failures()));
+return _st(self)._finished();
+})}));
+$1=_st($ctx1.locals.runner)._announcer();
+$2=(smalltalk.ResultAnnouncement || ResultAnnouncement);
+$3=(function(ann){
+return smalltalk.withContext(function($ctx2) { 
$4=_st(_st(ann)._result()).__eq_eq($ctx1.locals.result);
+$5=(function(){
+return smalltalk.withContext(function($ctx3) { 
$6=_st(_st($ctx1.locals.result)._runs()).__eq(_st($ctx1.locals.result)._total());
+return _st($6)._ifTrue_($ctx1.locals.assertBlock);
+})});
+return _st($4)._ifTrue_($5);
+})});
+_st($1)._on_do_($2,$3);
+_st($ctx1.locals.runner)._run();
+return self}, self, "testAsyncErrorsAndFailures", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "testAsyncErrorsAndFailures\x0a\x09| suite runner result assertBlock |\x0a\x09suite := #('fakeError' 'fakeErrorFailingInTearDown' 'fakeFailure' 'testPass') collect: [ :each | self class selector: each ].\x0a    runner := TestSuiteRunner on: suite.\x0a    self timeout: 200.\x0a\x09result := runner result.\x0a    assertBlock := self async: [\x0a\x09\x09self assert: #('fakeError') equals: (self sortedSelectors: result errors).\x0a\x09\x09self assert: #('fakeErrorFailingInTearDown' 'fakeFailure') equals: (self sortedSelectors: result failures).\x0a\x09\x09self finished\x0a  \x09].\x0a    runner announcer on: ResultAnnouncement do: [:ann |\x0a    \x09ann result == result  ifTrue: [ result runs = result total ifTrue: assertBlock ]].\x0a\x09runner run",
+messageSends: ["collect:", "selector:", "class", "on:", "timeout:", "result", "async:", "assert:equals:", "sortedSelectors:", "errors", "failures", "finished", "on:do:", "ifTrue:", "=", "total", "runs", "==", "announcer", "run"],
+referencedClasses: ["TestSuiteRunner", "ResultAnnouncement"]
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testAsyncNeedsTimeout",
+smalltalk.method({
+selector: "testAsyncNeedsTimeout",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._should_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._async_((function(){
+return smalltalk.withContext(function($ctx3) { 
})}));
+})}),(smalltalk.Error || Error));
+_st(self)._timeout_((0));
+_st(self)._shouldnt_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._async_((function(){
+return smalltalk.withContext(function($ctx3) { 
})}));
+})}),(smalltalk.Error || Error));
+_st(self)._finished();
+return self}, self, "testAsyncNeedsTimeout", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "testAsyncNeedsTimeout\x0a    self should: [ self async: [ ] ] raise: Error.\x0a    self timeout: 0.\x0a    self shouldnt: [ self async: [ ] ] raise: Error.\x0a    self finished",
+messageSends: ["should:raise:", "async:", "timeout:", "shouldnt:raise:", "finished"],
+referencedClasses: ["Error"]
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testFinishedNeedsTimeout",
+smalltalk.method({
+selector: "testFinishedNeedsTimeout",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._should_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._finished();
+})}),(smalltalk.Error || Error));
+_st(self)._timeout_((0));
+_st(self)._shouldnt_raise_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._finished();
+})}),(smalltalk.Error || Error));
+return self}, self, "testFinishedNeedsTimeout", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "testFinishedNeedsTimeout\x0a    self should: [ self finished ] raise: Error.\x0a    self timeout: 0.\x0a    self shouldnt: [ self finished ] raise: Error.",
+messageSends: ["should:raise:", "finished", "timeout:", "shouldnt:raise:"],
+referencedClasses: ["Error"]
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testIsAsyncReturnsCorrectValues",
+smalltalk.method({
+selector: "testIsAsyncReturnsCorrectValues",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._deny_(_st(self)._isAsync());
+_st(self)._timeout_((0));
+_st(self)._assert_(_st(self)._isAsync());
+_st(self)._finished();
+_st(self)._deny_(_st(self)._isAsync());
+return self}, self, "testIsAsyncReturnsCorrectValues", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "testIsAsyncReturnsCorrectValues\x0a    self deny: self isAsync.\x0a    self timeout: 0.\x0a    self assert: self isAsync.\x0a    self finished.\x0a    self deny: self isAsync",
+messageSends: ["deny:", "isAsync", "timeout:", "assert:", "finished"],
+referencedClasses: []
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testPass",
+smalltalk.method({
+selector: "testPass",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@flag"]="bad";
+_st(self)._timeout_((10));
+self["@flag"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._assert_(true);
+_st(self)._finished();
+self["@flag"]="ok";
+return self["@flag"];
+})})))._valueWithTimeout_((5));
+return self}, self, "testPass", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "testPass\x0a\x09flag := 'bad'.\x0a\x09self timeout: 10.\x0a    flag := (self async: [ self assert: true. self finished. flag := 'ok' ]) valueWithTimeout: 5",
+messageSends: ["timeout:", "valueWithTimeout:", "async:", "assert:", "finished"],
+referencedClasses: []
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testTimeouts",
+smalltalk.method({
+selector: "testTimeouts",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2,$4,$6,$5,$3;
+$ctx1.locals.suite=nil;
+$ctx1.locals.runner=nil;
+$ctx1.locals.result=nil;
+$ctx1.locals.assertBlock=nil;
+$ctx1.locals.suite=_st(["fakeTimeout", "fakeMultipleTimeoutFailing", "fakeMultipleTimeoutPassing", "testPass"])._collect_((function(each){
+return smalltalk.withContext(function($ctx2) { 
return _st(_st(self)._class())._selector_(each);
+})}));
+$ctx1.locals.runner=_st((smalltalk.TestSuiteRunner || TestSuiteRunner))._on_($ctx1.locals.suite);
+_st(self)._timeout_((200));
+$ctx1.locals.result=_st($ctx1.locals.runner)._result();
+$ctx1.locals.assertBlock=_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._assert_(_st(_st($ctx1.locals.result)._errors())._isEmpty());
+_st(self)._assert_equals_(["fakeMultipleTimeoutFailing", "fakeTimeout"],_st(self)._sortedSelectors_(_st($ctx1.locals.result)._failures()));
+return _st(self)._finished();
+})}));
+$1=_st($ctx1.locals.runner)._announcer();
+$2=(smalltalk.ResultAnnouncement || ResultAnnouncement);
+$3=(function(ann){
+return smalltalk.withContext(function($ctx2) { 
$4=_st(_st(ann)._result()).__eq_eq($ctx1.locals.result);
+$5=(function(){
+return smalltalk.withContext(function($ctx3) { 
$6=_st(_st($ctx1.locals.result)._runs()).__eq(_st($ctx1.locals.result)._total());
+return _st($6)._ifTrue_($ctx1.locals.assertBlock);
+})});
+return _st($4)._ifTrue_($5);
+})});
+_st($1)._on_do_($2,$3);
+_st($ctx1.locals.runner)._run();
+return self}, self, "testTimeouts", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "testTimeouts\x0a\x09| suite runner result assertBlock |\x0a\x09suite := #('fakeTimeout' 'fakeMultipleTimeoutFailing' 'fakeMultipleTimeoutPassing' 'testPass') collect: [ :each | self class selector: each ].\x0a    runner := TestSuiteRunner on: suite.\x0a    self timeout: 200.\x0a\x09result := runner result.\x0a    assertBlock := self async: [\x0a\x09\x09self assert: result errors isEmpty.\x0a\x09\x09self assert: #('fakeMultipleTimeoutFailing' 'fakeTimeout') equals: (self sortedSelectors: result failures).\x0a\x09\x09self finished\x0a  \x09].\x0a    runner announcer on: ResultAnnouncement do: [:ann |\x0a    \x09ann result == result  ifTrue: [ result runs = result total ifTrue: assertBlock ]].\x0a\x09runner run",
+messageSends: ["collect:", "selector:", "class", "on:", "timeout:", "result", "async:", "assert:", "isEmpty", "errors", "assert:equals:", "sortedSelectors:", "failures", "finished", "on:do:", "ifTrue:", "=", "total", "runs", "==", "announcer", "run"],
+referencedClasses: ["TestSuiteRunner", "ResultAnnouncement"]
+}),
+smalltalk.SUnitAsyncTest);
+
+smalltalk.addMethod(
+"_testTwoAsyncPassesWithFinishedOnlyOneIsRun",
+smalltalk.method({
+selector: "testTwoAsyncPassesWithFinishedOnlyOneIsRun",
+category: 'tests',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
$ctx1.locals.x=nil;
+self["@flag"]="bad";
+_st(self)._timeout_((10));
+$ctx1.locals.x=(0);
+self["@flag"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._finished();
+self["@flag"]="ok";
+self["@flag"];
+$ctx1.locals.x=_st($ctx1.locals.x).__plus((1));
+$ctx1.locals.x;
+return _st(self)._assert_equals_((1),$ctx1.locals.x);
+})})))._valueWithTimeout_((0));
+self["@flag"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self)._finished();
+self["@flag"]="ok";
+self["@flag"];
+$ctx1.locals.x=_st($ctx1.locals.x).__plus((1));
+$ctx1.locals.x;
+return _st(self)._assert_equals_((1),$ctx1.locals.x);
+})})))._valueWithTimeout_((0));
+return self}, self, "testTwoAsyncPassesWithFinishedOnlyOneIsRun", [], smalltalk.SUnitAsyncTest)},
+args: [],
+source: "testTwoAsyncPassesWithFinishedOnlyOneIsRun\x0a\x09| x |\x0a\x09flag := 'bad'.\x0a\x09self timeout: 10.\x0a    x := 0.\x0a    flag := (self async: [ self finished. flag := 'ok'. x := x+1. self assert: 1 equals: x ]) valueWithTimeout: 0.\x0a    flag := (self async: [ self finished. flag := 'ok'. x := x+1. self assert: 1 equals: x ]) valueWithTimeout: 0.",
+messageSends: ["timeout:", "valueWithTimeout:", "async:", "finished", "+", "assert:equals:"],
+referencedClasses: []
+}),
+smalltalk.SUnitAsyncTest);
+
+
+

+ 346 - 47
js/SUnit.deploy.js

@@ -6,7 +6,9 @@ smalltalk.method({
 selector: "result",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@result"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@result"];
+return $1;
 }, self, "result", [], smalltalk.ResultAnnouncement)}
 }),
 smalltalk.ResultAnnouncement);
@@ -24,7 +26,7 @@ smalltalk.ResultAnnouncement);
 
 
 
-smalltalk.addClass('TestCase', smalltalk.Object, ['testSelector'], 'SUnit');
+smalltalk.addClass('TestCase', smalltalk.Object, ['testSelector', 'asyncTimeout', 'context'], 'SUnit');
 smalltalk.addMethod(
 "_assert_",
 smalltalk.method({
@@ -42,7 +44,9 @@ smalltalk.method({
 selector: "assert:description:",
 fn: function (aBoolean,aString){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
if(! smalltalk.assert(aBoolean)){
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=aBoolean;
+if(! smalltalk.assert($1)){
 _st(self)._signalFailure_(aString);
 };
 return self}, self, "assert:description:", [aBoolean,aString], smalltalk.TestCase)}
@@ -62,6 +66,38 @@ return $1;
 }),
 smalltalk.TestCase);
 
+smalltalk.addMethod(
+"_async_",
+smalltalk.method({
+selector: "async:",
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$ctx1.locals.c=nil;
+_st(self)._errorIfNotAsync_("#async");
+$ctx1.locals.c=self["@context"];
+$1=(function(){
+return smalltalk.withContext(function($ctx2) { 
$2=_st(self)._isAsync();
+if(smalltalk.assert($2)){
+return _st($ctx1.locals.c)._execute_(aBlock);
+};
+})});
+return $1;
+}, self, "async:", [aBlock], smalltalk.TestCase)}
+}),
+smalltalk.TestCase);
+
+smalltalk.addMethod(
+"_context_",
+smalltalk.method({
+selector: "context:",
+fn: function (aRunningTestContext){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@context"]=aRunningTestContext;
+return self}, self, "context:", [aRunningTestContext], smalltalk.TestCase)}
+}),
+smalltalk.TestCase);
+
 smalltalk.addMethod(
 "_deny_",
 smalltalk.method({
@@ -73,13 +109,54 @@ return self}, self, "deny:", [aBoolean], smalltalk.TestCase)}
 }),
 smalltalk.TestCase);
 
+smalltalk.addMethod(
+"_errorIfNotAsync_",
+smalltalk.method({
+selector: "errorIfNotAsync:",
+fn: function (aString){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(self)._isAsync();
+if(! smalltalk.assert($1)){
+_st(self)._error_(_st(aString).__comma(" used without prior #timeout:"));
+};
+return self}, self, "errorIfNotAsync:", [aString], smalltalk.TestCase)}
+}),
+smalltalk.TestCase);
+
+smalltalk.addMethod(
+"_finished",
+smalltalk.method({
+selector: "finished",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._errorIfNotAsync_("#finished");
+self["@asyncTimeout"]=nil;
+return self}, self, "finished", [], smalltalk.TestCase)}
+}),
+smalltalk.TestCase);
+
+smalltalk.addMethod(
+"_isAsync",
+smalltalk.method({
+selector: "isAsync",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(self["@asyncTimeout"])._notNil();
+return $1;
+}, self, "isAsync", [], smalltalk.TestCase)}
+}),
+smalltalk.TestCase);
+
 smalltalk.addMethod(
 "_performTest",
 smalltalk.method({
 selector: "performTest",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
_st(self)._perform_(_st(self)._selector());
+return smalltalk.withContext(function($ctx1) { 
self["@asyncTimeout"]=nil;
+_st(self)._perform_(_st(self)._selector());
 return self}, self, "performTest", [], smalltalk.TestCase)}
 }),
 smalltalk.TestCase);
@@ -90,12 +167,7 @@ smalltalk.method({
 selector: "runCase",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
_st((function(){
-return smalltalk.withContext(function($ctx2) { 
_st(self)._setUp();
-return _st(self)._performTest();
-})}))._ensure_((function(){
-return smalltalk.withContext(function($ctx2) { 
return _st(self)._tearDown();
-})}));
+return smalltalk.withContext(function($ctx1) { 
_st(_st((smalltalk.TestContext || TestContext))._testCase_(self))._start();
 return self}, self, "runCase", [], smalltalk.TestCase)}
 }),
 smalltalk.TestCase);
@@ -106,7 +178,9 @@ smalltalk.method({
 selector: "selector",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@testSelector"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@testSelector"];
+return $1;
 }, self, "selector", [], smalltalk.TestCase)}
 }),
 smalltalk.TestCase);
@@ -199,6 +273,27 @@ return smalltalk.withContext(function($ctx1) { 
return self}, self, "tearDown",
 }),
 smalltalk.TestCase);
 
+smalltalk.addMethod(
+"_timeout_",
+smalltalk.method({
+selector: "timeout:",
+fn: function (aNumber){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@asyncTimeout"];
+if(($receiver = $1) == nil || $receiver == undefined){
+$1;
+} else {
+_st(self["@asyncTimeout"])._clearTimeout();
+};
+self["@asyncTimeout"]=(0);
+self["@asyncTimeout"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._assert_description_(false,"SUnit grace time exhausted");
+})})))._valueWithTimeout_(aNumber);
+return self}, self, "timeout:", [aNumber], smalltalk.TestCase)}
+}),
+smalltalk.TestCase);
+
 
 smalltalk.addMethod(
 "_allTestSelectors",
@@ -206,14 +301,15 @@ smalltalk.method({
 selector: "allTestSelectors",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$ctx1.selectors=nil;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+$ctx1.locals.selectors=nil;
 $ctx1.locals.selectors=_st(self)._testSelectors();
 $1=_st(self)._shouldInheritSelectors();
 if(smalltalk.assert($1)){
 _st($ctx1.locals.selectors)._addAll_(_st(_st(self)._superclass())._allTestSelectors());
 };
-return $ctx1.locals.selectors;
+$2=$ctx1.locals.selectors;
+return $2;
 }, self, "allTestSelectors", [], smalltalk.TestCase.klass)}
 }),
 smalltalk.TestCase.klass);
@@ -252,7 +348,9 @@ smalltalk.method({
 selector: "lookupHierarchyRoot",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return (smalltalk.TestCase || TestCase);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=(smalltalk.TestCase || TestCase);
+return $1;
 }, self, "lookupHierarchyRoot", [], smalltalk.TestCase.klass)}
 }),
 smalltalk.TestCase.klass);
@@ -302,6 +400,167 @@ return $1;
 smalltalk.TestCase.klass);
 
 
+smalltalk.addClass('TestContext', smalltalk.Object, ['testCase'], 'SUnit');
+smalltalk.addMethod(
+"_execute_",
+smalltalk.method({
+selector: "execute:",
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$2;
+$ctx1.locals.failed=nil;
+_st(self["@testCase"])._context_(self);
+$1=(function(){
+return smalltalk.withContext(function($ctx2) { 
$ctx1.locals.failed=true;
+$ctx1.locals.failed;
+_st(aBlock)._value();
+$ctx1.locals.failed=false;
+return $ctx1.locals.failed;
+})});
+$2=(function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self["@testCase"])._context_(nil);
+$3=_st($ctx1.locals.failed)._and_((function(){
+return smalltalk.withContext(function($ctx3) { 
return _st(self["@testCase"])._isAsync();
+})}));
+if(smalltalk.assert($3)){
+_st(self["@testCase"])._finished();
+};
+$4=_st(self["@testCase"])._isAsync();
+if(! smalltalk.assert($4)){
+return _st(self["@testCase"])._tearDown();
+};
+})});
+_st($1)._ensure_($2);
+return self}, self, "execute:", [aBlock], smalltalk.TestContext)}
+}),
+smalltalk.TestContext);
+
+smalltalk.addMethod(
+"_start",
+smalltalk.method({
+selector: "start",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._execute_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self["@testCase"])._setUp();
+return _st(self["@testCase"])._performTest();
+})}));
+return self}, self, "start", [], smalltalk.TestContext)}
+}),
+smalltalk.TestContext);
+
+smalltalk.addMethod(
+"_testCase_",
+smalltalk.method({
+selector: "testCase:",
+fn: function (aTestCase){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@testCase"]=aTestCase;
+return self}, self, "testCase:", [aTestCase], smalltalk.TestContext)}
+}),
+smalltalk.TestContext);
+
+
+smalltalk.addMethod(
+"_testCase_",
+smalltalk.method({
+selector: "testCase:",
+fn: function (aTestCase){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
+$2=_st(self)._new();
+_st($2)._testCase_(aTestCase);
+$3=_st($2)._yourself();
+$1=$3;
+return $1;
+}, self, "testCase:", [aTestCase], smalltalk.TestContext.klass)}
+}),
+smalltalk.TestContext.klass);
+
+
+smalltalk.addClass('ReportingTestContext', smalltalk.TestContext, ['finished', 'result'], 'SUnit');
+smalltalk.addMethod(
+"_execute_",
+smalltalk.method({
+selector: "execute:",
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2;
+$1=(function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._withErrorReporting_((function(){
+return smalltalk.withContext(function($ctx3) { 
return smalltalk.TestContext.fn.prototype._execute_.apply(_st(self), [aBlock]);
+})}));
+})});
+$2=(function(){
+return smalltalk.withContext(function($ctx2) { 
$3=_st(self["@testCase"])._isAsync();
+if(! smalltalk.assert($3)){
+_st(self["@result"])._increaseRuns();
+return _st(self["@finished"])._value();
+};
+})});
+_st($1)._ensure_($2);
+return self}, self, "execute:", [aBlock], smalltalk.ReportingTestContext)}
+}),
+smalltalk.ReportingTestContext);
+
+smalltalk.addMethod(
+"_finished_",
+smalltalk.method({
+selector: "finished:",
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@finished"]=aBlock;
+return self}, self, "finished:", [aBlock], smalltalk.ReportingTestContext)}
+}),
+smalltalk.ReportingTestContext);
+
+smalltalk.addMethod(
+"_result_",
+smalltalk.method({
+selector: "result:",
+fn: function (aTestResult){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@result"]=aTestResult;
+return self}, self, "result:", [aTestResult], smalltalk.ReportingTestContext)}
+}),
+smalltalk.ReportingTestContext);
+
+smalltalk.addMethod(
+"_withErrorReporting_",
+smalltalk.method({
+selector: "withErrorReporting:",
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(aBlock)._on_do_((smalltalk.TestFailure || TestFailure),(function(ex){
+return smalltalk.withContext(function($ctx3) { 
return _st(self["@result"])._addFailure_(self["@testCase"]);
+})}));
+})}))._on_do_((smalltalk.Error || Error),(function(ex){
+return smalltalk.withContext(function($ctx2) { 
return _st(self["@result"])._addError_(self["@testCase"]);
+})}));
+return self}, self, "withErrorReporting:", [aBlock], smalltalk.ReportingTestContext)}
+}),
+smalltalk.ReportingTestContext);
+
+
+smalltalk.addMethod(
+"_testCase_result_finished_",
+smalltalk.method({
+selector: "testCase:result:finished:",
+fn: function (aTestCase,aTestResult,aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
+$2=smalltalk.TestContext.klass.fn.prototype._testCase_.apply(_st(self), [aTestCase]);
+_st($2)._result_(aTestResult);
+_st($2)._finished_(aBlock);
+$3=_st($2)._yourself();
+$1=$3;
+return $1;
+}, self, "testCase:result:finished:", [aTestCase,aTestResult,aBlock], smalltalk.ReportingTestContext.klass)}
+}),
+smalltalk.ReportingTestContext.klass);
+
+
 smalltalk.addClass('TestFailure', smalltalk.Error, [], 'SUnit');
 
 
@@ -334,7 +593,9 @@ smalltalk.method({
 selector: "errors",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@errors"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@errors"];
+return $1;
 }, self, "errors", [], smalltalk.TestResult)}
 }),
 smalltalk.TestResult);
@@ -345,7 +606,9 @@ smalltalk.method({
 selector: "failures",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@failures"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@failures"];
+return $1;
 }, self, "failures", [], smalltalk.TestResult)}
 }),
 smalltalk.TestResult);
@@ -419,7 +682,9 @@ smalltalk.method({
 selector: "runs",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@runs"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@runs"];
+return $1;
 }, self, "runs", [], smalltalk.TestResult)}
 }),
 smalltalk.TestResult);
@@ -430,18 +695,19 @@ smalltalk.method({
 selector: "status",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
+return smalltalk.withContext(function($ctx1) { 
var $2,$4,$3,$1;
 $2=_st(_st(self)._errors())._isEmpty();
-if(smalltalk.assert($2)){
-$3=_st(_st(self)._failures())._isEmpty();
-if(smalltalk.assert($3)){
-$1="success";
-} else {
-$1="failure";
-};
+$3=(function(){
+return smalltalk.withContext(function($ctx2) { 
$4=_st(_st(self)._failures())._isEmpty();
+if(smalltalk.assert($4)){
+return "success";
 } else {
-$1="error";
+return "failure";
 };
+})});
+$1=_st($2)._ifTrue_ifFalse_($3,(function(){
+return smalltalk.withContext(function($ctx2) { 
return "error";
+})}));
 return $1;
 }, self, "status", [], smalltalk.TestResult)}
 }),
@@ -453,7 +719,9 @@ smalltalk.method({
 selector: "timestamp",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@timestamp"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@timestamp"];
+return $1;
 }, self, "timestamp", [], smalltalk.TestResult)}
 }),
 smalltalk.TestResult);
@@ -464,7 +732,9 @@ smalltalk.method({
 selector: "total",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@total"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@total"];
+return $1;
 }, self, "total", [], smalltalk.TestResult)}
 }),
 smalltalk.TestResult);
@@ -482,27 +752,54 @@ smalltalk.TestResult);
 
 
 
-smalltalk.addClass('TestSuiteRunner', smalltalk.Object, ['suite', 'result', 'announcer'], 'SUnit');
+smalltalk.addClass('TestSuiteRunner', smalltalk.Object, ['suite', 'result', 'announcer', 'runNextTest'], 'SUnit');
 smalltalk.addMethod(
 "_announcer",
 smalltalk.method({
 selector: "announcer",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@announcer"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@announcer"];
+return $1;
 }, self, "announcer", [], smalltalk.TestSuiteRunner)}
 }),
 smalltalk.TestSuiteRunner);
 
+smalltalk.addMethod(
+"_contextOf_",
+smalltalk.method({
+selector: "contextOf:",
+fn: function (anInteger){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st((smalltalk.ReportingTestContext || ReportingTestContext))._testCase_result_finished_(_st(self["@suite"])._at_(anInteger),self["@result"],(function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._resume();
+})}));
+return $1;
+}, self, "contextOf:", [anInteger], smalltalk.TestSuiteRunner)}
+}),
+smalltalk.TestSuiteRunner);
+
 smalltalk.addMethod(
 "_initialize",
 smalltalk.method({
 selector: "initialize",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
smalltalk.Object.fn.prototype._initialize.apply(_st(self), []);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+smalltalk.Object.fn.prototype._initialize.apply(_st(self), []);
 self["@announcer"]=_st((smalltalk.Announcer || Announcer))._new();
 self["@result"]=_st((smalltalk.TestResult || TestResult))._new();
+self["@runNextTest"]=(function(){
+return smalltalk.withContext(function($ctx2) { 
$ctx2.locals.runs=nil;
+$ctx2.locals.runs=_st(self["@result"])._runs();
+$ctx2.locals.runs;
+$1=_st($ctx2.locals.runs).__lt(_st(self["@result"])._total());
+if(smalltalk.assert($1)){
+return _st(_st(self)._contextOf_(_st($ctx2.locals.runs).__plus((1))))._start();
+};
+})});
 return self}, self, "initialize", [], smalltalk.TestSuiteRunner)}
 }),
 smalltalk.TestSuiteRunner);
@@ -513,31 +810,33 @@ smalltalk.method({
 selector: "result",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@result"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@result"];
+return $1;
 }, self, "result", [], smalltalk.TestSuiteRunner)}
 }),
 smalltalk.TestSuiteRunner);
 
+smalltalk.addMethod(
+"_resume",
+smalltalk.method({
+selector: "resume",
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self["@runNextTest"])._fork();
+_st(self["@announcer"])._announce_(_st(_st((smalltalk.ResultAnnouncement || ResultAnnouncement))._new())._result_(self["@result"]));
+return self}, self, "resume", [], smalltalk.TestSuiteRunner)}
+}),
+smalltalk.TestSuiteRunner);
+
 smalltalk.addMethod(
 "_run",
 smalltalk.method({
 selector: "run",
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.worker=nil;
-_st(self["@result"])._total_(_st(self["@suite"])._size());
-_st(self["@announcer"])._announce_(_st(_st((smalltalk.ResultAnnouncement || ResultAnnouncement))._new())._result_(self["@result"]));
-$ctx1.locals.worker=(function(){
-return smalltalk.withContext(function($ctx2) { 
return _st(self["@result"])._nextRunDo_((function(index){
-return smalltalk.withContext(function($ctx3) { 
return _st((function(){
-return smalltalk.withContext(function($ctx4) { 
return _st(self["@result"])._runCase_(_st(self["@suite"])._at_(index));
-})}))._ensure_((function(){
-return smalltalk.withContext(function($ctx4) { 
_st($ctx1.locals.worker)._fork();
-return _st(self["@announcer"])._announce_(_st(_st((smalltalk.ResultAnnouncement || ResultAnnouncement))._new())._result_(self["@result"]));
-})}));
-})}));
-})});
-_st($ctx1.locals.worker)._fork();
+return smalltalk.withContext(function($ctx1) { 
_st(self["@result"])._total_(_st(self["@suite"])._size());
+_st(self)._resume();
 return self}, self, "run", [], smalltalk.TestSuiteRunner)}
 }),
 smalltalk.TestSuiteRunner);

+ 445 - 56
js/SUnit.js

@@ -7,7 +7,9 @@ selector: "result",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@result"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@result"];
+return $1;
 }, self, "result", [], smalltalk.ResultAnnouncement)},
 args: [],
 source: "result\x0a\x09^result",
@@ -34,7 +36,8 @@ smalltalk.ResultAnnouncement);
 
 
 
-smalltalk.addClass('TestCase', smalltalk.Object, ['testSelector'], 'SUnit');
+smalltalk.addClass('TestCase', smalltalk.Object, ['testSelector', 'asyncTimeout', 'context'], 'SUnit');
+smalltalk.TestCase.comment="A TestCase is an implementation of the command pattern to run a test.  \x0a\x0a`TestCase` instances are created with the class method `#selector:`, \x0apassing the symbol that names the method to be executed when the test case runs.\x0a\x0aWhen you discover a new fixture, subclass `TestCase` and create a `#test...` method for the first test.  \x0aAs that method develops and more `#test...` methods are added, you will find yourself refactoring temps \x0ainto instance variables for the objects in the fixture and overriding `#setUp` to initialize these variables.  \x0aAs required, override `#tearDown` to nil references, release objects and deallocate."
 smalltalk.addMethod(
 "_assert_",
 smalltalk.method({
@@ -58,7 +61,9 @@ selector: "assert:description:",
 category: 'testing',
 fn: function (aBoolean,aString){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
if(! smalltalk.assert(aBoolean)){
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=aBoolean;
+if(! smalltalk.assert($1)){
 _st(self)._signalFailure_(aString);
 };
 return self}, self, "assert:description:", [aBoolean,aString], smalltalk.TestCase)},
@@ -87,6 +92,48 @@ referencedClasses: []
 }),
 smalltalk.TestCase);
 
+smalltalk.addMethod(
+"_async_",
+smalltalk.method({
+selector: "async:",
+category: 'async',
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$1;
+$ctx1.locals.c=nil;
+_st(self)._errorIfNotAsync_("#async");
+$ctx1.locals.c=self["@context"];
+$1=(function(){
+return smalltalk.withContext(function($ctx2) { 
$2=_st(self)._isAsync();
+if(smalltalk.assert($2)){
+return _st($ctx1.locals.c)._execute_(aBlock);
+};
+})});
+return $1;
+}, self, "async:", [aBlock], smalltalk.TestCase)},
+args: ["aBlock"],
+source: "async: aBlock\x0a\x09| c |\x0a\x09self errorIfNotAsync: '#async'.\x0a    c := context.\x0a    ^ [ self isAsync ifTrue: [ c execute: aBlock ] ]",
+messageSends: ["errorIfNotAsync:", "ifTrue:", "execute:", "isAsync"],
+referencedClasses: []
+}),
+smalltalk.TestCase);
+
+smalltalk.addMethod(
+"_context_",
+smalltalk.method({
+selector: "context:",
+category: 'accessing',
+fn: function (aRunningTestContext){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@context"]=aRunningTestContext;
+return self}, self, "context:", [aRunningTestContext], smalltalk.TestCase)},
+args: ["aRunningTestContext"],
+source: "context: aRunningTestContext\x0a\x09context := aRunningTestContext",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.TestCase);
+
 smalltalk.addMethod(
 "_deny_",
 smalltalk.method({
@@ -103,6 +150,61 @@ referencedClasses: []
 }),
 smalltalk.TestCase);
 
+smalltalk.addMethod(
+"_errorIfNotAsync_",
+smalltalk.method({
+selector: "errorIfNotAsync:",
+category: 'error handling',
+fn: function (aString){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(self)._isAsync();
+if(! smalltalk.assert($1)){
+_st(self)._error_(_st(aString).__comma(" used without prior #timeout:"));
+};
+return self}, self, "errorIfNotAsync:", [aString], smalltalk.TestCase)},
+args: ["aString"],
+source: "errorIfNotAsync: aString\x0a\x09self isAsync ifFalse: [ \x0a    \x09self error: aString, ' used without prior #timeout:' ]",
+messageSends: ["ifFalse:", "error:", ",", "isAsync"],
+referencedClasses: []
+}),
+smalltalk.TestCase);
+
+smalltalk.addMethod(
+"_finished",
+smalltalk.method({
+selector: "finished",
+category: 'async',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._errorIfNotAsync_("#finished");
+self["@asyncTimeout"]=nil;
+return self}, self, "finished", [], smalltalk.TestCase)},
+args: [],
+source: "finished\x0a\x09self errorIfNotAsync: '#finished'.\x0a\x09asyncTimeout := nil",
+messageSends: ["errorIfNotAsync:"],
+referencedClasses: []
+}),
+smalltalk.TestCase);
+
+smalltalk.addMethod(
+"_isAsync",
+smalltalk.method({
+selector: "isAsync",
+category: 'testing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st(self["@asyncTimeout"])._notNil();
+return $1;
+}, self, "isAsync", [], smalltalk.TestCase)},
+args: [],
+source: "isAsync\x0a\x09^asyncTimeout notNil",
+messageSends: ["notNil"],
+referencedClasses: []
+}),
+smalltalk.TestCase);
+
 smalltalk.addMethod(
 "_performTest",
 smalltalk.method({
@@ -110,10 +212,11 @@ selector: "performTest",
 category: 'running',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
_st(self)._perform_(_st(self)._selector());
+return smalltalk.withContext(function($ctx1) { 
self["@asyncTimeout"]=nil;
+_st(self)._perform_(_st(self)._selector());
 return self}, self, "performTest", [], smalltalk.TestCase)},
 args: [],
-source: "performTest\x0a\x09self perform: self selector\x0a",
+source: "performTest\x0a\x09asyncTimeout := nil.\x0a\x09self perform: self selector",
 messageSends: ["perform:", "selector"],
 referencedClasses: []
 }),
@@ -126,17 +229,12 @@ selector: "runCase",
 category: 'running',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
_st((function(){
-return smalltalk.withContext(function($ctx2) { 
_st(self)._setUp();
-return _st(self)._performTest();
-})}))._ensure_((function(){
-return smalltalk.withContext(function($ctx2) { 
return _st(self)._tearDown();
-})}));
+return smalltalk.withContext(function($ctx1) { 
_st(_st((smalltalk.TestContext || TestContext))._testCase_(self))._start();
 return self}, self, "runCase", [], smalltalk.TestCase)},
 args: [],
-source: "runCase\x0a\x09[\x09self setUp.\x0a\x09\x09self performTest ] ensure: [\x0a\x09\x09self tearDown.\x0a\x09\x09\x22self cleanUpInstanceVariables\x22 ]\x0a",
-messageSends: ["ensure:", "tearDown", "setUp", "performTest"],
-referencedClasses: []
+source: "runCase\x0a\x09\x22Runs a test case in isolated context, leaking all errors.\x22\x0a\x0a\x09(TestContext testCase: self) start",
+messageSends: ["start", "testCase:"],
+referencedClasses: ["TestContext"]
 }),
 smalltalk.TestCase);
 
@@ -147,7 +245,9 @@ selector: "selector",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@testSelector"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@testSelector"];
+return $1;
 }, self, "selector", [], smalltalk.TestCase)},
 args: [],
 source: "selector\x0a\x09^testSelector",
@@ -279,6 +379,32 @@ referencedClasses: []
 }),
 smalltalk.TestCase);
 
+smalltalk.addMethod(
+"_timeout_",
+smalltalk.method({
+selector: "timeout:",
+category: 'async',
+fn: function (aNumber){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@asyncTimeout"];
+if(($receiver = $1) == nil || $receiver == undefined){
+$1;
+} else {
+_st(self["@asyncTimeout"])._clearTimeout();
+};
+self["@asyncTimeout"]=(0);
+self["@asyncTimeout"]=_st(_st(self)._async_((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._assert_description_(false,"SUnit grace time exhausted");
+})})))._valueWithTimeout_(aNumber);
+return self}, self, "timeout:", [aNumber], smalltalk.TestCase)},
+args: ["aNumber"],
+source: "timeout: aNumber\x0a\x09\x22Set a grace time timeout in milliseconds to run the test asynchronously\x22\x0a    \x0a\x09asyncTimeout ifNotNil: [ asyncTimeout clearTimeout ].\x0a    \x0a     \x22to allow #async: message send without throwing an error\x22\x0a\x09asyncTimeout := 0.\x0a    \x0a\x09asyncTimeout := (self async: [ \x0a    \x09self assert: false description: 'SUnit grace time exhausted' ])\x0a        \x09valueWithTimeout: aNumber",
+messageSends: ["ifNotNil:", "clearTimeout", "valueWithTimeout:", "async:", "assert:description:"],
+referencedClasses: []
+}),
+smalltalk.TestCase);
+
 
 smalltalk.addMethod(
 "_allTestSelectors",
@@ -287,14 +413,15 @@ selector: "allTestSelectors",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $1;
-$ctx1.selectors=nil;
+return smalltalk.withContext(function($ctx1) { 
var $1,$2;
+$ctx1.locals.selectors=nil;
 $ctx1.locals.selectors=_st(self)._testSelectors();
 $1=_st(self)._shouldInheritSelectors();
 if(smalltalk.assert($1)){
 _st($ctx1.locals.selectors)._addAll_(_st(_st(self)._superclass())._allTestSelectors());
 };
-return $ctx1.locals.selectors;
+$2=$ctx1.locals.selectors;
+return $2;
 }, self, "allTestSelectors", [], smalltalk.TestCase.klass)},
 args: [],
 source: "allTestSelectors\x0a\x09| selectors |\x0a\x09selectors := self testSelectors.\x0a\x09self shouldInheritSelectors ifTrue: [\x0a\x09\x09selectors addAll: self superclass allTestSelectors].\x0a\x09^selectors",
@@ -348,7 +475,9 @@ selector: "lookupHierarchyRoot",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return (smalltalk.TestCase || TestCase);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=(smalltalk.TestCase || TestCase);
+return $1;
 }, self, "lookupHierarchyRoot", [], smalltalk.TestCase.klass)},
 args: [],
 source: "lookupHierarchyRoot\x0a\x09^TestCase",
@@ -417,10 +546,220 @@ referencedClasses: []
 smalltalk.TestCase.klass);
 
 
+smalltalk.addClass('TestContext', smalltalk.Object, ['testCase'], 'SUnit');
+smalltalk.TestContext.comment="TestContext governs running a particular test case.\x0a\x0aIt's main added value is `#execute:` method which runs a block\x0aas a part of test case (restores context, nilling it afterwards,\x0acleaning/calling tearDown as appropriate for sync/async scenario)."
+smalltalk.addMethod(
+"_execute_",
+smalltalk.method({
+selector: "execute:",
+category: 'running',
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1,$3,$4,$2;
+$ctx1.locals.failed=nil;
+_st(self["@testCase"])._context_(self);
+$1=(function(){
+return smalltalk.withContext(function($ctx2) { 
$ctx1.locals.failed=true;
+$ctx1.locals.failed;
+_st(aBlock)._value();
+$ctx1.locals.failed=false;
+return $ctx1.locals.failed;
+})});
+$2=(function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self["@testCase"])._context_(nil);
+$3=_st($ctx1.locals.failed)._and_((function(){
+return smalltalk.withContext(function($ctx3) { 
return _st(self["@testCase"])._isAsync();
+})}));
+if(smalltalk.assert($3)){
+_st(self["@testCase"])._finished();
+};
+$4=_st(self["@testCase"])._isAsync();
+if(! smalltalk.assert($4)){
+return _st(self["@testCase"])._tearDown();
+};
+})});
+_st($1)._ensure_($2);
+return self}, self, "execute:", [aBlock], smalltalk.TestContext)},
+args: ["aBlock"],
+source: "execute: aBlock\x0a\x09| failed |\x0a    \x0a    testCase context: self.\x0a    [ \x0a    \x09failed := true. \x0a        aBlock value. \x0a        failed := false \x0a\x09] \x0a    \x09ensure: [\x0a        \x09testCase context: nil.\x0a            \x0a        \x09(failed and: [ testCase isAsync ]) ifTrue: [ \x0a            \x09testCase finished ].\x0a        \x09testCase isAsync ifFalse: [ \x0a        \x09\x09testCase tearDown ] ]",
+messageSends: ["context:", "ensure:", "ifTrue:", "finished", "and:", "isAsync", "ifFalse:", "tearDown", "value"],
+referencedClasses: []
+}),
+smalltalk.TestContext);
+
+smalltalk.addMethod(
+"_start",
+smalltalk.method({
+selector: "start",
+category: 'running',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self)._execute_((function(){
+return smalltalk.withContext(function($ctx2) { 
_st(self["@testCase"])._setUp();
+return _st(self["@testCase"])._performTest();
+})}));
+return self}, self, "start", [], smalltalk.TestContext)},
+args: [],
+source: "start\x0a\x09self execute: [ \x0a    \x09testCase setUp. \x0a        testCase performTest ]",
+messageSends: ["execute:", "setUp", "performTest"],
+referencedClasses: []
+}),
+smalltalk.TestContext);
+
+smalltalk.addMethod(
+"_testCase_",
+smalltalk.method({
+selector: "testCase:",
+category: 'accessing',
+fn: function (aTestCase){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@testCase"]=aTestCase;
+return self}, self, "testCase:", [aTestCase], smalltalk.TestContext)},
+args: ["aTestCase"],
+source: "testCase: aTestCase\x0a\x09testCase := aTestCase",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.TestContext);
+
+
+smalltalk.addMethod(
+"_testCase_",
+smalltalk.method({
+selector: "testCase:",
+category: 'instance creation',
+fn: function (aTestCase){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
+$2=_st(self)._new();
+_st($2)._testCase_(aTestCase);
+$3=_st($2)._yourself();
+$1=$3;
+return $1;
+}, self, "testCase:", [aTestCase], smalltalk.TestContext.klass)},
+args: ["aTestCase"],
+source: "testCase: aTestCase\x0a\x09^self new\x0a        testCase: aTestCase;\x0a        yourself",
+messageSends: ["testCase:", "new", "yourself"],
+referencedClasses: []
+}),
+smalltalk.TestContext.klass);
+
+
+smalltalk.addClass('ReportingTestContext', smalltalk.TestContext, ['finished', 'result'], 'SUnit');
+smalltalk.ReportingTestContext.comment="ReportingTestContext adds `TestResult` reporting\x0ato `TestContext`.\x0a\x0aErrors are caught and save into a `TestResult`,\x0aWhen test case is finished (which can be later for async tests),\x0aa callback block is executed; this is used by a `TestSuiteRunner`."
+smalltalk.addMethod(
+"_execute_",
+smalltalk.method({
+selector: "execute:",
+category: 'running',
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1,$3,$2;
+$1=(function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._withErrorReporting_((function(){
+return smalltalk.withContext(function($ctx3) { 
return smalltalk.TestContext.fn.prototype._execute_.apply(_st(self), [aBlock]);
+})}));
+})});
+$2=(function(){
+return smalltalk.withContext(function($ctx2) { 
$3=_st(self["@testCase"])._isAsync();
+if(! smalltalk.assert($3)){
+_st(self["@result"])._increaseRuns();
+return _st(self["@finished"])._value();
+};
+})});
+_st($1)._ensure_($2);
+return self}, self, "execute:", [aBlock], smalltalk.ReportingTestContext)},
+args: ["aBlock"],
+source: "execute: aBlock\x0a    [ \x0a    \x09self withErrorReporting: [ super execute: aBlock ] \x0a\x09]\x0a    \x09ensure: [ \x0a        \x09testCase isAsync ifFalse: [ \x0a            \x09result increaseRuns. finished value ] ]",
+messageSends: ["ensure:", "ifFalse:", "increaseRuns", "value", "isAsync", "withErrorReporting:", "execute:"],
+referencedClasses: []
+}),
+smalltalk.ReportingTestContext);
+
+smalltalk.addMethod(
+"_finished_",
+smalltalk.method({
+selector: "finished:",
+category: 'accessing',
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@finished"]=aBlock;
+return self}, self, "finished:", [aBlock], smalltalk.ReportingTestContext)},
+args: ["aBlock"],
+source: "finished: aBlock\x0a\x09finished := aBlock",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ReportingTestContext);
+
+smalltalk.addMethod(
+"_result_",
+smalltalk.method({
+selector: "result:",
+category: 'accessing',
+fn: function (aTestResult){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
self["@result"]=aTestResult;
+return self}, self, "result:", [aTestResult], smalltalk.ReportingTestContext)},
+args: ["aTestResult"],
+source: "result: aTestResult\x0a\x09result := aTestResult",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.ReportingTestContext);
+
+smalltalk.addMethod(
+"_withErrorReporting_",
+smalltalk.method({
+selector: "withErrorReporting:",
+category: 'private',
+fn: function (aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st((function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(aBlock)._on_do_((smalltalk.TestFailure || TestFailure),(function(ex){
+return smalltalk.withContext(function($ctx3) { 
return _st(self["@result"])._addFailure_(self["@testCase"]);
+})}));
+})}))._on_do_((smalltalk.Error || Error),(function(ex){
+return smalltalk.withContext(function($ctx2) { 
return _st(self["@result"])._addError_(self["@testCase"]);
+})}));
+return self}, self, "withErrorReporting:", [aBlock], smalltalk.ReportingTestContext)},
+args: ["aBlock"],
+source: "withErrorReporting: aBlock\x0a \x09[ aBlock\x0a\x09\x09on: TestFailure \x0a\x09\x09do: [ :ex | result addFailure: testCase ] \x0a\x09]\x0a    \x09on: Error \x0a        do: [ :ex | result addError: testCase ]",
+messageSends: ["on:do:", "addError:", "addFailure:"],
+referencedClasses: ["Error", "TestFailure"]
+}),
+smalltalk.ReportingTestContext);
+
+
+smalltalk.addMethod(
+"_testCase_result_finished_",
+smalltalk.method({
+selector: "testCase:result:finished:",
+category: 'instance creation',
+fn: function (aTestCase,aTestResult,aBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
+$2=smalltalk.TestContext.klass.fn.prototype._testCase_.apply(_st(self), [aTestCase]);
+_st($2)._result_(aTestResult);
+_st($2)._finished_(aBlock);
+$3=_st($2)._yourself();
+$1=$3;
+return $1;
+}, self, "testCase:result:finished:", [aTestCase,aTestResult,aBlock], smalltalk.ReportingTestContext.klass)},
+args: ["aTestCase", "aTestResult", "aBlock"],
+source: "testCase: aTestCase result: aTestResult finished: aBlock\x0a\x09^(super testCase: aTestCase)\x0a        result: aTestResult;\x0a        finished: aBlock;\x0a        yourself",
+messageSends: ["result:", "testCase:", "finished:", "yourself"],
+referencedClasses: []
+}),
+smalltalk.ReportingTestContext.klass);
+
+
 smalltalk.addClass('TestFailure', smalltalk.Error, [], 'SUnit');
+smalltalk.TestFailure.comment="The test framework distinguishes between failures and errors.  \x0aA failure is an event whose possibiity is explicitly anticipated and checked for in an assertion, \x0awhereas an error is an unanticipated problem like a division by 0 or an index out of bounds.  \x0a\x0aTestFailure is raised when the boolean parameter of an #`assert:` or `#deny:` call is the opposite of what the assertion claims."
 
 
 smalltalk.addClass('TestResult', smalltalk.Object, ['timestamp', 'runs', 'errors', 'failures', 'total'], 'SUnit');
+smalltalk.TestResult.comment="A TestResult implements the collecting parameter pattern for running a bunch of tests.  \x0a\x0aA TestResult holds tests that have run, sorted into the result categories of passed, failures and errors.\x0a\x0aTestResult is an interesting object to subclass or substitute. `#runCase:` is the external protocol you need to reproduce"
 smalltalk.addMethod(
 "_addError_",
 smalltalk.method({
@@ -460,7 +799,9 @@ selector: "errors",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@errors"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@errors"];
+return $1;
 }, self, "errors", [], smalltalk.TestResult)},
 args: [],
 source: "errors\x0a\x09^errors",
@@ -476,7 +817,9 @@ selector: "failures",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@failures"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@failures"];
+return $1;
 }, self, "failures", [], smalltalk.TestResult)},
 args: [],
 source: "failures\x0a\x09^failures",
@@ -575,7 +918,9 @@ selector: "runs",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@runs"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@runs"];
+return $1;
 }, self, "runs", [], smalltalk.TestResult)},
 args: [],
 source: "runs\x0a\x09^runs",
@@ -591,18 +936,19 @@ selector: "status",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
var $2,$3,$1;
+return smalltalk.withContext(function($ctx1) { 
var $2,$4,$3,$1;
 $2=_st(_st(self)._errors())._isEmpty();
-if(smalltalk.assert($2)){
-$3=_st(_st(self)._failures())._isEmpty();
-if(smalltalk.assert($3)){
-$1="success";
-} else {
-$1="failure";
-};
+$3=(function(){
+return smalltalk.withContext(function($ctx2) { 
$4=_st(_st(self)._failures())._isEmpty();
+if(smalltalk.assert($4)){
+return "success";
 } else {
-$1="error";
+return "failure";
 };
+})});
+$1=_st($2)._ifTrue_ifFalse_($3,(function(){
+return smalltalk.withContext(function($ctx2) { 
return "error";
+})}));
 return $1;
 }, self, "status", [], smalltalk.TestResult)},
 args: [],
@@ -619,7 +965,9 @@ selector: "timestamp",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@timestamp"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@timestamp"];
+return $1;
 }, self, "timestamp", [], smalltalk.TestResult)},
 args: [],
 source: "timestamp\x0a\x09^timestamp",
@@ -635,7 +983,9 @@ selector: "total",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@total"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@total"];
+return $1;
 }, self, "total", [], smalltalk.TestResult)},
 args: [],
 source: "total\x0a\x09^total",
@@ -662,7 +1012,7 @@ smalltalk.TestResult);
 
 
 
-smalltalk.addClass('TestSuiteRunner', smalltalk.Object, ['suite', 'result', 'announcer'], 'SUnit');
+smalltalk.addClass('TestSuiteRunner', smalltalk.Object, ['suite', 'result', 'announcer', 'runNextTest'], 'SUnit');
 smalltalk.addMethod(
 "_announcer",
 smalltalk.method({
@@ -670,7 +1020,9 @@ selector: "announcer",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@announcer"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@announcer"];
+return $1;
 }, self, "announcer", [], smalltalk.TestSuiteRunner)},
 args: [],
 source: "announcer\x0a\x09^announcer",
@@ -679,6 +1031,26 @@ referencedClasses: []
 }),
 smalltalk.TestSuiteRunner);
 
+smalltalk.addMethod(
+"_contextOf_",
+smalltalk.method({
+selector: "contextOf:",
+category: 'private',
+fn: function (anInteger){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=_st((smalltalk.ReportingTestContext || ReportingTestContext))._testCase_result_finished_(_st(self["@suite"])._at_(anInteger),self["@result"],(function(){
+return smalltalk.withContext(function($ctx2) { 
return _st(self)._resume();
+})}));
+return $1;
+}, self, "contextOf:", [anInteger], smalltalk.TestSuiteRunner)},
+args: ["anInteger"],
+source: "contextOf: anInteger\x0a   \x09^ReportingTestContext testCase: (suite at: anInteger) result: result finished: [ self resume ]",
+messageSends: ["testCase:result:finished:", "at:", "resume"],
+referencedClasses: ["ReportingTestContext"]
+}),
+smalltalk.TestSuiteRunner);
+
 smalltalk.addMethod(
 "_initialize",
 smalltalk.method({
@@ -686,13 +1058,23 @@ selector: "initialize",
 category: 'initialization',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
smalltalk.Object.fn.prototype._initialize.apply(_st(self), []);
+return smalltalk.withContext(function($ctx1) { 
var $1;
+smalltalk.Object.fn.prototype._initialize.apply(_st(self), []);
 self["@announcer"]=_st((smalltalk.Announcer || Announcer))._new();
 self["@result"]=_st((smalltalk.TestResult || TestResult))._new();
+self["@runNextTest"]=(function(){
+return smalltalk.withContext(function($ctx2) { 
$ctx2.locals.runs=nil;
+$ctx2.locals.runs=_st(self["@result"])._runs();
+$ctx2.locals.runs;
+$1=_st($ctx2.locals.runs).__lt(_st(self["@result"])._total());
+if(smalltalk.assert($1)){
+return _st(_st(self)._contextOf_(_st($ctx2.locals.runs).__plus((1))))._start();
+};
+})});
 return self}, self, "initialize", [], smalltalk.TestSuiteRunner)},
 args: [],
-source: "initialize\x0a\x09super initialize.\x0a\x09announcer := Announcer new.\x0a    result := TestResult new",
-messageSends: ["initialize", "new"],
+source: "initialize\x0a\x09super initialize.\x0a\x09announcer := Announcer new.\x0a    result := TestResult new.\x0a    runNextTest := [ | runs | runs := result runs. runs < result total ifTrue: [ (self contextOf: runs + 1) start ]].",
+messageSends: ["initialize", "new", "runs", "ifTrue:", "start", "contextOf:", "+", "<", "total"],
 referencedClasses: ["Announcer", "TestResult"]
 }),
 smalltalk.TestSuiteRunner);
@@ -704,7 +1086,9 @@ selector: "result",
 category: 'accessing',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
return self["@result"];
+return smalltalk.withContext(function($ctx1) { 
var $1;
+$1=self["@result"];
+return $1;
 }, self, "result", [], smalltalk.TestSuiteRunner)},
 args: [],
 source: "result\x0a\x09^result",
@@ -713,6 +1097,23 @@ referencedClasses: []
 }),
 smalltalk.TestSuiteRunner);
 
+smalltalk.addMethod(
+"_resume",
+smalltalk.method({
+selector: "resume",
+category: 'actions',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
_st(self["@runNextTest"])._fork();
+_st(self["@announcer"])._announce_(_st(_st((smalltalk.ResultAnnouncement || ResultAnnouncement))._new())._result_(self["@result"]));
+return self}, self, "resume", [], smalltalk.TestSuiteRunner)},
+args: [],
+source: "resume\x0a\x09runNextTest fork.\x0a    announcer announce: (ResultAnnouncement new result: result)",
+messageSends: ["fork", "announce:", "result:", "new"],
+referencedClasses: ["ResultAnnouncement"]
+}),
+smalltalk.TestSuiteRunner);
+
 smalltalk.addMethod(
 "_run",
 smalltalk.method({
@@ -720,25 +1121,13 @@ selector: "run",
 category: 'actions',
 fn: function (){
 var self=this;
-return smalltalk.withContext(function($ctx1) { 
$ctx1.worker=nil;
-_st(self["@result"])._total_(_st(self["@suite"])._size());
-_st(self["@announcer"])._announce_(_st(_st((smalltalk.ResultAnnouncement || ResultAnnouncement))._new())._result_(self["@result"]));
-$ctx1.locals.worker=(function(){
-return smalltalk.withContext(function($ctx2) { 
return _st(self["@result"])._nextRunDo_((function(index){
-return smalltalk.withContext(function($ctx3) { 
return _st((function(){
-return smalltalk.withContext(function($ctx4) { 
return _st(self["@result"])._runCase_(_st(self["@suite"])._at_(index));
-})}))._ensure_((function(){
-return smalltalk.withContext(function($ctx4) { 
_st($ctx1.locals.worker)._fork();
-return _st(self["@announcer"])._announce_(_st(_st((smalltalk.ResultAnnouncement || ResultAnnouncement))._new())._result_(self["@result"]));
-})}));
-})}));
-})});
-_st($ctx1.locals.worker)._fork();
+return smalltalk.withContext(function($ctx1) { 
_st(self["@result"])._total_(_st(self["@suite"])._size());
+_st(self)._resume();
 return self}, self, "run", [], smalltalk.TestSuiteRunner)},
 args: [],
-source: "run\x0a\x09| worker |\x0a\x09result total: suite size.\x0a    announcer announce: (ResultAnnouncement new result: result).\x0a    worker := [ result nextRunDo: [ :index |\x0a\x09\x09[ result runCase: (suite at: index) ]\x0a\x09\x09ensure: [ worker fork.\x0a        \x09announcer announce: (ResultAnnouncement new result: result) ]]].\x0a\x09worker fork",
-messageSends: ["total:", "size", "announce:", "result:", "new", "nextRunDo:", "ensure:", "fork", "runCase:", "at:"],
-referencedClasses: ["ResultAnnouncement"]
+source: "run\x0a\x09result total: suite size.\x0a\x09self resume",
+messageSends: ["total:", "size", "resume"],
+referencedClasses: []
 }),
 smalltalk.TestSuiteRunner);
 

+ 32 - 31
js/amber.js

@@ -30,7 +30,7 @@ amber = (function() {
 			smalltalk.TabManager._current()._open();
 		}
 		return false;
-	}
+	};
 
 	that.load = function(obj) {
 		spec = obj || {};
@@ -98,7 +98,8 @@ amber = (function() {
 				'IDE',
 				'Examples',
 				'Benchfib',
-				'Kernel-Tests'
+				'Kernel-Tests',
+				'SUnit-Tests'
 			]);
 		}
 
@@ -114,9 +115,9 @@ amber = (function() {
 	};
 
 	function loadPackages(names, prefix, urlHome){
-		var name, url;
-		var prefix = prefix || 'js';
-		var urlHome = urlHome || home;
+		var name;
+		prefix = prefix || 'js';
+		urlHome = urlHome || home;
 
 		for (var i=0; i < names.length; i++) {
 			name = names[i].split(/\.js$/)[0];
@@ -127,12 +128,12 @@ amber = (function() {
 			js: urlHome+prefix,
 			st: urlHome+'st'
 		};
-	};
+	}
 
 	function addJSToLoad(name, prefix, urlHome) {
-		var urlHome = urlHome || home;
+		urlHome = urlHome || home;
 		jsToLoad.push(buildJSURL(name, prefix, urlHome));
-	};
+	}
 
 	function resolve(base, path) {
 		if (/(^|:)\/\//.test(path)) {
@@ -157,9 +158,8 @@ amber = (function() {
 	}
 
 	function buildJSURL(name, prefix, urlHome) {
-		var prefix = prefix || '';
-		var name = name;
-		var urlHome = urlHome || home;
+		prefix = prefix || '';
+		urlHome = urlHome || home;
 
 		var parts = name.match(/^(.*\/)([^/]*)$/);
 		if (parts) {
@@ -172,11 +172,10 @@ amber = (function() {
 		}
 
 		return urlHome + prefix + '/' + name;
-	};
+	}
 
 	function loadCSS(name, prefix) {
-		var prefix = prefix || 'css';
-		var name = name;
+		prefix = prefix || 'css';
 		if (!deploy) {
 			name = name + nocache;
 		}
@@ -188,7 +187,7 @@ amber = (function() {
 		link.setAttribute("type", "text/css");
 		link.setAttribute("href", url);
 		document.getElementsByTagName("head")[0].appendChild(link);
-	};
+	}
 
 	function loadDependencies() {
 		if (typeof jQuery == 'undefined') {
@@ -198,7 +197,7 @@ amber = (function() {
 		if ((typeof jQuery == 'undefined') || (typeof jQuery.ui == 'undefined')) {      
 			writeScriptTag(buildJSURL('js/lib/jQuery/jquery-ui-1.8.16.custom.min.js'));
 		}
-	};
+	}
 
 	function loadIDEDependencies() {
 		addJSToLoad('js/lib/jQuery/jquery.textarea.js');
@@ -206,7 +205,7 @@ amber = (function() {
 		addJSToLoad('js/lib/CodeMirror/smalltalk.js');
 		loadCSS('lib/CodeMirror/codemirror.css', 'js');
 		loadCSS('lib/CodeMirror/amber.css', 'js');
-	};
+	}
 
 	// This will be called after JS files have been loaded
 	function initializeSmalltalk(commitPath) {
@@ -217,14 +216,14 @@ amber = (function() {
 			}
 			if (spec.ready) {
 				spec.ready();
-			};
+			}
 			evaluateSmalltalkScripts();
 		};
 
-		loadAllJS(); 
-	};
+		loadAllJS();
+	}
 
-	/* 
+	/*
 	 * When loaded using AJAX, scripts order not guaranteed.
 	 * Load JS in the order they have been added using addJSToLoad().
 	 * If loaded, will use jQuery's getScript instead of adding a script element
@@ -235,7 +234,7 @@ amber = (function() {
 			loadJS = loadJSViaJQuery;
 		}
 		loadNextJS();
-	};
+	}
 
 	function loadNextJS() {
 		loadJS(jsToLoad[0], function(){
@@ -244,26 +243,26 @@ amber = (function() {
 				loadNextJS();
 			}
 		});
-	};
+	}
 
 	function loadJSViaScriptTag(url, callback) {
 		writeScriptTag(url);
 		callback();
-	};
+	}
 
 	function loadJSViaJQuery(url, callback) {
 		$.ajax({
 			dataType: "script",
-			url: jsToLoad[0],
+			url: url,
 			cache: deploy,
 			success: callback
 		});
-	};
+	}
 
 	function writeScriptTag(src) {
 		var scriptString = '<script src="' + src + '" type="text/javascript"></script>';
 		document.write(scriptString);
-	};
+	}
 
 	function evaluateSmalltalkScripts() {
 		jQuery(document).ready(function() {
@@ -274,11 +273,13 @@ amber = (function() {
 					[jQuery(elt).html()])
 			});
 		})
-	};
+	}
+
+	var localPackages;
 
 	function populateLocalPackages(){
 		var localStorageRE = /^smalltalk\.packages\.(.*)$/;
-		localPackages = {};
+		var localPackages = {};
 
 		var match, key;
 
@@ -291,14 +292,14 @@ amber = (function() {
 		}
 
 		return localPackages;
-	};
+	}
 
 	function clearLocalPackages() {
 		for (var name in localPackages) {
 			log('Removing ' + name + ' from local storage');
 			localStorage.removeItem('smalltalk.packages.' + name);
 		}
-	};
+	}
 
 	function log(string) {
 		if (debug) {

+ 31 - 4
st/Kernel-Methods.st

@@ -120,11 +120,17 @@ fork
 !
 
 valueWithInterval: aNumber
-	<return setInterval(self, aNumber)>
+	<
+    	var interval = setInterval(self, aNumber);
+    	return smalltalk.Timeout._on_(interval);
+    >
 !
 
 valueWithTimeout: aNumber
-	<return setTimeout(self, aNumber)>
+	<
+    	var timeout = setTimeout(self, aNumber);
+    	return smalltalk.Timeout._on_(timeout);
+    >
 ! !
 
 Object subclass: #CompiledMethod
@@ -215,6 +221,21 @@ source: aString
 Object subclass: #ForkPool
 	instanceVariableNames: 'poolSize maxPoolSize queue worker'
 	package: 'Kernel-Methods'!
+!ForkPool commentStamp!
+A ForkPool is responsible for handling forked blocks.
+The pool size sets the maximum concurrent forked blocks.
+
+The default instance is accessed with `ForkPool default`!
+
+!ForkPool methodsFor: 'accessing'!
+
+maxPoolSize
+	^ maxPoolSize ifNil: [ self defaultMaxPoolSize ]
+!
+
+maxPoolSize: anInteger
+	maxPoolSize := anInteger
+! !
 
 !ForkPool methodsFor: 'action'!
 
@@ -224,16 +245,22 @@ addWorker
 !
 
 fork: aBlock
-	poolSize < maxPoolSize ifTrue: [ self addWorker ].
+	poolSize < self maxPoolSize ifTrue: [ self addWorker ].
 	queue back: aBlock
 ! !
 
+!ForkPool methodsFor: 'defaults'!
+
+defaultMaxPoolSize
+	^ self class defaultMaxPoolSize
+! !
+
 !ForkPool methodsFor: 'initialization'!
 
 initialize
     super initialize.
+    
 	poolSize := 0.
-    maxPoolSize := self class defaultMaxPoolSize.
     queue := Queue new.
     worker := self makeWorker
 !

+ 36 - 10
st/Kernel-Objects.st

@@ -1102,16 +1102,6 @@ positive
 	^ self >= 0
 ! !
 
-!Number methodsFor: 'timeouts/intervals'!
-
-clearInterval
-	<clearInterval(Number(self))>
-!
-
-clearTimeout
-	<clearTimeout(Number(self))>
-! !
-
 !Number class methodsFor: 'instance creation'!
 
 pi
@@ -1620,6 +1610,42 @@ current
 	<return smalltalk>
 ! !
 
+Object subclass: #Timeout
+	instanceVariableNames: 'rawTimeout'
+	package: 'Kernel-Objects'!
+!Timeout commentStamp!
+I am wrapping the returns from set{Timeout,Interval}.
+
+Number suffices in browsers, but node.js returns an object.!
+
+!Timeout methodsFor: 'accessing'!
+
+rawTimeout: anObject
+	rawTimeout := anObject
+! !
+
+!Timeout methodsFor: 'timeout/interval'!
+
+clearInterval
+	<
+    	var interval = self["@rawTimeout"];
+		clearInterval(interval);
+    >
+!
+
+clearTimeout
+	<
+    	var timeout = self["@rawTimeout"];
+		clearTimeout(timeout);
+    >
+! !
+
+!Timeout class methodsFor: 'instance creation'!
+
+on: anObject
+	^self new rawTimeout: anObject; yourself
+! !
+
 Object subclass: #UndefinedObject
 	instanceVariableNames: ''
 	package: 'Kernel-Objects'!

+ 8 - 0
st/Kernel-Tests.st

@@ -5,6 +5,14 @@ TestCase subclass: #BlockClosureTest
 
 !BlockClosureTest methodsFor: 'tests'!
 
+testCanClearInterval
+	self shouldnt: [([Error new signal] valueWithInterval: 0) clearInterval] raise: Error
+!
+
+testCanClearTimeout
+	self shouldnt: [([Error new signal] valueWithTimeout: 0) clearTimeout] raise: Error
+!
+
 testCompiledSource
 	self assert: ([1+1] compiledSource includesSubString: 'function')
 !

+ 186 - 0
st/SUnit-Tests.st

@@ -0,0 +1,186 @@
+Smalltalk current createPackage: 'SUnit-Tests' properties: #{}!
+TestCase subclass: #ExampleSetTest
+	instanceVariableNames: 'empty full'
+	package: 'SUnit-Tests'!
+!ExampleSetTest commentStamp!
+ExampleSetTest is taken from Pharo 1.4.
+
+THe purpose of this class is to demonstrate a simple use case of the test framework.!
+
+!ExampleSetTest methodsFor: 'running'!
+
+setUp
+	empty := Set new.
+	full := Set with: 5 with: #abc
+! !
+
+!ExampleSetTest methodsFor: 'tests'!
+
+testAdd
+	empty add: 5.
+	self assert: (empty includes: 5)
+!
+
+testGrow
+	empty addAll: (1 to: 100).
+	self assert: empty size = 100
+!
+
+testIllegal
+	self 
+		should: [empty at: 5] 
+		raise: Error.
+	self 
+		should: [empty at: 5 put: #abc] 
+		raise: Error
+!
+
+testIncludes
+	self assert: (full includes: 5).
+	self assert: (full includes: #abc)
+!
+
+testOccurrences
+	self assert: (empty occurrencesOf: 0) = 0.
+	self assert: (full occurrencesOf: 5) = 1.
+	full add: 5.
+	self assert: (full occurrencesOf: 5) = 1
+!
+
+testRemove
+	full remove: 5.
+	self assert: (full includes: #abc).
+	self deny: (full includes: 5)
+! !
+
+TestCase subclass: #SUnitAsyncTest
+	instanceVariableNames: 'flag'
+	package: 'SUnit-Tests'!
+
+!SUnitAsyncTest methodsFor: 'helpers'!
+
+fakeError
+	flag := 'bad'.
+	self timeout: 10.
+    flag := (self async: [ flag := 'ok'. self error: 'Intentional' ]) valueWithTimeout: 5
+!
+
+fakeErrorFailingInTearDown
+	flag := 'bad'.
+	self timeout: 10.
+    flag := (self async: [ self error: 'Intentional' ]) valueWithTimeout: 5
+!
+
+fakeFailure
+	flag := 'bad'.
+	self timeout: 10.
+    flag := (self async: [ flag := 'ok'. self assert: false ]) valueWithTimeout: 5
+!
+
+fakeMultipleTimeoutFailing
+	self timeout: 100.
+    (self async: [
+		self timeout: 5.
+        (self async: [ self finished ]) valueWithTimeout: 10
+	]) valueWithTimeout: 5
+!
+
+fakeMultipleTimeoutPassing
+	self timeout: 10.
+    (self async: [
+		self timeout: 20.
+        (self async: [ self finished ]) valueWithTimeout: 10
+	]) valueWithTimeout: 5
+!
+
+fakeTimeout
+	self timeout: 4.
+    (self async: [ self finished ]) valueWithTimeout: 5
+! !
+
+!SUnitAsyncTest methodsFor: 'private'!
+
+sortedSelectors: aCollection
+	^(aCollection collect: [:each | each selector]) sorted
+! !
+
+!SUnitAsyncTest methodsFor: 'running'!
+
+setUp
+	flag := 'ok'
+!
+
+tearDown
+	self assert: 'ok' equals: flag
+! !
+
+!SUnitAsyncTest methodsFor: 'tests'!
+
+testAsyncErrorsAndFailures
+	| suite runner result assertBlock |
+	suite := #('fakeError' 'fakeErrorFailingInTearDown' 'fakeFailure' 'testPass') collect: [ :each | self class selector: each ].
+    runner := TestSuiteRunner on: suite.
+    self timeout: 200.
+	result := runner result.
+    assertBlock := self async: [
+		self assert: #('fakeError') equals: (self sortedSelectors: result errors).
+		self assert: #('fakeErrorFailingInTearDown' 'fakeFailure') equals: (self sortedSelectors: result failures).
+		self finished
+  	].
+    runner announcer on: ResultAnnouncement do: [:ann |
+    	ann result == result  ifTrue: [ result runs = result total ifTrue: assertBlock ]].
+	runner run
+!
+
+testAsyncNeedsTimeout
+    self should: [ self async: [ ] ] raise: Error.
+    self timeout: 0.
+    self shouldnt: [ self async: [ ] ] raise: Error.
+    self finished
+!
+
+testFinishedNeedsTimeout
+    self should: [ self finished ] raise: Error.
+    self timeout: 0.
+    self shouldnt: [ self finished ] raise: Error.
+!
+
+testIsAsyncReturnsCorrectValues
+    self deny: self isAsync.
+    self timeout: 0.
+    self assert: self isAsync.
+    self finished.
+    self deny: self isAsync
+!
+
+testPass
+	flag := 'bad'.
+	self timeout: 10.
+    flag := (self async: [ self assert: true. self finished. flag := 'ok' ]) valueWithTimeout: 5
+!
+
+testTimeouts
+	| suite runner result assertBlock |
+	suite := #('fakeTimeout' 'fakeMultipleTimeoutFailing' 'fakeMultipleTimeoutPassing' 'testPass') collect: [ :each | self class selector: each ].
+    runner := TestSuiteRunner on: suite.
+    self timeout: 200.
+	result := runner result.
+    assertBlock := self async: [
+		self assert: result errors isEmpty.
+		self assert: #('fakeMultipleTimeoutFailing' 'fakeTimeout') equals: (self sortedSelectors: result failures).
+		self finished
+  	].
+    runner announcer on: ResultAnnouncement do: [:ann |
+    	ann result == result  ifTrue: [ result runs = result total ifTrue: assertBlock ]].
+	runner run
+!
+
+testTwoAsyncPassesWithFinishedOnlyOneIsRun
+	| x |
+	flag := 'bad'.
+	self timeout: 10.
+    x := 0.
+    flag := (self async: [ self finished. flag := 'ok'. x := x+1. self assert: 1 equals: x ]) valueWithTimeout: 0.
+    flag := (self async: [ self finished. flag := 'ok'. x := x+1. self assert: 1 equals: x ]) valueWithTimeout: 0.
+! !
+

+ 186 - 14
st/SUnit.st

@@ -14,11 +14,25 @@ result: aTestResult
 ! !
 
 Object subclass: #TestCase
-	instanceVariableNames: 'testSelector'
+	instanceVariableNames: 'testSelector asyncTimeout context'
 	package: 'SUnit'!
+!TestCase commentStamp!
+A TestCase is an implementation of the command pattern to run a test.  
+
+`TestCase` instances are created with the class method `#selector:`, 
+passing the symbol that names the method to be executed when the test case runs.
+
+When you discover a new fixture, subclass `TestCase` and create a `#test...` method for the first test.  
+As that method develops and more `#test...` methods are added, you will find yourself refactoring temps 
+into instance variables for the objects in the fixture and overriding `#setUp` to initialize these variables.  
+As required, override `#tearDown` to nil references, release objects and deallocate.!
 
 !TestCase methodsFor: 'accessing'!
 
+context: aRunningTestContext
+	context := aRunningTestContext
+!
+
 selector
 	^testSelector
 !
@@ -27,6 +41,40 @@ setTestSelector: aSelector
 	testSelector := aSelector
 ! !
 
+!TestCase methodsFor: 'async'!
+
+async: aBlock
+	| c |
+	self errorIfNotAsync: '#async'.
+    c := context.
+    ^ [ self isAsync ifTrue: [ c execute: aBlock ] ]
+!
+
+finished
+	self errorIfNotAsync: '#finished'.
+	asyncTimeout := nil
+!
+
+timeout: aNumber
+	"Set a grace time timeout in milliseconds to run the test asynchronously"
+    
+	asyncTimeout ifNotNil: [ asyncTimeout clearTimeout ].
+    
+     "to allow #async: message send without throwing an error"
+	asyncTimeout := 0.
+    
+	asyncTimeout := (self async: [ 
+    	self assert: false description: 'SUnit grace time exhausted' ])
+        	valueWithTimeout: aNumber
+! !
+
+!TestCase methodsFor: 'error handling'!
+
+errorIfNotAsync: aString
+	self isAsync ifFalse: [ 
+    	self error: aString, ' used without prior #timeout:' ]
+! !
+
 !TestCase methodsFor: 'private'!
 
 signalFailure: aString
@@ -38,14 +86,14 @@ signalFailure: aString
 !TestCase methodsFor: 'running'!
 
 performTest
+	asyncTimeout := nil.
 	self perform: self selector
 !
 
 runCase
-	[	self setUp.
-		self performTest ] ensure: [
-		self tearDown.
-		"self cleanUpInstanceVariables" ]
+	"Runs a test case in isolated context, leaking all errors."
+
+	(TestContext testCase: self) start
 !
 
 setUp
@@ -72,6 +120,10 @@ deny: aBoolean
 	self assert: aBoolean not
 !
 
+isAsync
+	^asyncTimeout notNil
+!
+
 should: aBlock
 	self assert: aBlock value
 !
@@ -126,13 +178,127 @@ shouldInheritSelectors
 	^self ~= self lookupHierarchyRoot
 ! !
 
+Object subclass: #TestContext
+	instanceVariableNames: 'testCase'
+	package: 'SUnit'!
+!TestContext commentStamp!
+TestContext governs running a particular test case.
+
+It's main added value is `#execute:` method which runs a block
+as a part of test case (restores context, nilling it afterwards,
+cleaning/calling tearDown as appropriate for sync/async scenario).!
+
+!TestContext methodsFor: 'accessing'!
+
+testCase: aTestCase
+	testCase := aTestCase
+! !
+
+!TestContext methodsFor: 'running'!
+
+execute: aBlock
+	| failed |
+    
+    testCase context: self.
+    [ 
+    	failed := true. 
+        aBlock value. 
+        failed := false 
+	] 
+    	ensure: [
+        	testCase context: nil.
+            
+        	(failed and: [ testCase isAsync ]) ifTrue: [ 
+            	testCase finished ].
+        	testCase isAsync ifFalse: [ 
+        		testCase tearDown ] ]
+!
+
+start
+	self execute: [ 
+    	testCase setUp. 
+        testCase performTest ]
+! !
+
+!TestContext class methodsFor: 'instance creation'!
+
+testCase: aTestCase
+	^self new
+        testCase: aTestCase;
+        yourself
+! !
+
+TestContext subclass: #ReportingTestContext
+	instanceVariableNames: 'finished result'
+	package: 'SUnit'!
+!ReportingTestContext commentStamp!
+ReportingTestContext adds `TestResult` reporting
+to `TestContext`.
+
+Errors are caught and save into a `TestResult`,
+When test case is finished (which can be later for async tests),
+a callback block is executed; this is used by a `TestSuiteRunner`.!
+
+!ReportingTestContext methodsFor: 'accessing'!
+
+finished: aBlock
+	finished := aBlock
+!
+
+result: aTestResult
+	result := aTestResult
+! !
+
+!ReportingTestContext methodsFor: 'private'!
+
+withErrorReporting: aBlock
+ 	[ aBlock
+		on: TestFailure 
+		do: [ :ex | result addFailure: testCase ] 
+	]
+    	on: Error 
+        do: [ :ex | result addError: testCase ]
+! !
+
+!ReportingTestContext methodsFor: 'running'!
+
+execute: aBlock
+    [ 
+    	self withErrorReporting: [ super execute: aBlock ] 
+	]
+    	ensure: [ 
+        	testCase isAsync ifFalse: [ 
+            	result increaseRuns. finished value ] ]
+! !
+
+!ReportingTestContext class methodsFor: 'instance creation'!
+
+testCase: aTestCase result: aTestResult finished: aBlock
+	^(super testCase: aTestCase)
+        result: aTestResult;
+        finished: aBlock;
+        yourself
+! !
+
 Error subclass: #TestFailure
 	instanceVariableNames: ''
 	package: 'SUnit'!
+!TestFailure commentStamp!
+The test framework distinguishes between failures and errors.  
+A failure is an event whose possibiity is explicitly anticipated and checked for in an assertion, 
+whereas an error is an unanticipated problem like a division by 0 or an index out of bounds.  
+
+TestFailure is raised when the boolean parameter of an #`assert:` or `#deny:` call is the opposite of what the assertion claims.!
 
 Object subclass: #TestResult
 	instanceVariableNames: 'timestamp runs errors failures total'
 	package: 'SUnit'!
+!TestResult commentStamp!
+A TestResult implements the collecting parameter pattern for running a bunch of tests.  
+
+A TestResult holds tests that have run, sorted into the result categories of passed, failures and errors.
+
+TestResult is an interesting object to subclass or substitute. `#runCase:` is the external protocol you need to reproduce!
 
 !TestResult methodsFor: 'accessing'!
 
@@ -209,7 +375,7 @@ runCase: aTestCase
 ! !
 
 Object subclass: #TestSuiteRunner
-	instanceVariableNames: 'suite result announcer'
+	instanceVariableNames: 'suite result announcer runNextTest'
 	package: 'SUnit'!
 
 !TestSuiteRunner methodsFor: 'accessing'!
@@ -228,15 +394,14 @@ suite: aCollection
 
 !TestSuiteRunner methodsFor: 'actions'!
 
+resume
+	runNextTest fork.
+    announcer announce: (ResultAnnouncement new result: result)
+!
+
 run
-	| worker |
 	result total: suite size.
-    announcer announce: (ResultAnnouncement new result: result).
-    worker := [ result nextRunDo: [ :index |
-		[ result runCase: (suite at: index) ]
-		ensure: [ worker fork.
-        	announcer announce: (ResultAnnouncement new result: result) ]]].
-	worker fork
+	self resume
 ! !
 
 !TestSuiteRunner methodsFor: 'initialization'!
@@ -244,7 +409,14 @@ run
 initialize
 	super initialize.
 	announcer := Announcer new.
-    result := TestResult new
+    result := TestResult new.
+    runNextTest := [ | runs | runs := result runs. runs < result total ifTrue: [ (self contextOf: runs + 1) start ]].
+! !
+
+!TestSuiteRunner methodsFor: 'private'!
+
+contextOf: anInteger
+   	^ReportingTestContext testCase: (suite at: anInteger) result: result finished: [ self resume ]
 ! !
 
 !TestSuiteRunner class methodsFor: 'instance creation'!

+ 1 - 1
test/run_build.sh

@@ -3,7 +3,7 @@ cd `dirname ${0}`/..
 
 
 # Create the build file
-cat js/boot.js js/Kernel-Announcements.js js/Kernel-Classes.js js/Kernel-Collections.js  js/Kernel-Exceptions.js js/Kernel-Methods.js js/Kernel-Objects.js js/Kernel-Transcript.js js/Compiler-AST.js js/Compiler-Core.js js/Compiler-Exceptions.js js/Compiler-IR.js js/Compiler-Inlining.js js/Compiler-Semantic.js js/SUnit.js js/Kernel-Tests.js js/Compiler-Tests.js test/Test.js js/parser.js js/init.js > test/run.js
+cat js/boot.js js/Kernel-Announcements.js js/Kernel-Classes.js js/Kernel-Collections.js  js/Kernel-Exceptions.js js/Kernel-Methods.js js/Kernel-Objects.js js/Kernel-Transcript.js js/Compiler-AST.js js/Compiler-Core.js js/Compiler-Exceptions.js js/Compiler-IR.js js/Compiler-Inlining.js js/Compiler-Semantic.js js/SUnit.js js/Kernel-Tests.js js/Compiler-Tests.js js/SUnit-Tests.js test/Test.js js/parser.js js/init.js > test/run.js
 
 
 #run it!