smalltalk: SUnit port (TestCase/TestSuite/TestResult/TestFailure) + 19 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled
This commit is contained in:
@@ -482,6 +482,25 @@
|
|||||||
;; (no asString here — Symbol/String have their own primitive
|
;; (no asString here — Symbol/String have their own primitive
|
||||||
;; impls; SequenceableCollection-level fallback would overwrite
|
;; impls; SequenceableCollection-level fallback would overwrite
|
||||||
;; the bare-name-for-Symbol behaviour.)
|
;; the bare-name-for-Symbol behaviour.)
|
||||||
|
;; Array class-side constructors for small fixed-arity literals.
|
||||||
|
(st-class-add-class-method! "Array" "with:"
|
||||||
|
(st-parse-method
|
||||||
|
"with: x | a | a := Array new: 1. a at: 1 put: x. ^ a"))
|
||||||
|
(st-class-add-class-method! "Array" "with:with:"
|
||||||
|
(st-parse-method
|
||||||
|
"with: a with: b
|
||||||
|
| r | r := Array new: 2.
|
||||||
|
r at: 1 put: a. r at: 2 put: b. ^ r"))
|
||||||
|
(st-class-add-class-method! "Array" "with:with:with:"
|
||||||
|
(st-parse-method
|
||||||
|
"with: a with: b with: c
|
||||||
|
| r | r := Array new: 3.
|
||||||
|
r at: 1 put: a. r at: 2 put: b. r at: 3 put: c. ^ r"))
|
||||||
|
(st-class-add-class-method! "Array" "with:with:with:with:"
|
||||||
|
(st-parse-method
|
||||||
|
"with: a with: b with: c with: d
|
||||||
|
| r | r := Array new: 4.
|
||||||
|
r at: 1 put: a. r at: 2 put: b. r at: 3 put: c. r at: 4 put: d. ^ r"))
|
||||||
;; ── HashedCollection / Set / Dictionary ──
|
;; ── HashedCollection / Set / Dictionary ──
|
||||||
;; Implemented as user instances with array-backed storage. Sets
|
;; Implemented as user instances with array-backed storage. Sets
|
||||||
;; use a single `array` ivar; Dictionaries use parallel `keys`/
|
;; use a single `array` ivar; Dictionaries use parallel `keys`/
|
||||||
|
|||||||
153
lib/smalltalk/sunit.sx
Normal file
153
lib/smalltalk/sunit.sx
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
;; SUnit — minimal port written in SX-Smalltalk, run by smalltalk-load.
|
||||||
|
;;
|
||||||
|
;; Provides:
|
||||||
|
;; TestCase — base class. Subclass it, add `testSomething` methods.
|
||||||
|
;; TestSuite — a collection of TestCase instances; runs them all.
|
||||||
|
;; TestResult — passes / failures / errors counts and lists.
|
||||||
|
;; TestFailure — Error subclass raised by `assert:` and friends.
|
||||||
|
;;
|
||||||
|
;; Conventions:
|
||||||
|
;; - Test methods are run in a fresh instance per test.
|
||||||
|
;; - `setUp` is sent before each test; `tearDown` after.
|
||||||
|
;; - Failures are signalled by TestFailure; runner catches and records.
|
||||||
|
|
||||||
|
(define
|
||||||
|
st-sunit-source
|
||||||
|
"Error subclass: #TestFailure
|
||||||
|
instanceVariableNames: ''!
|
||||||
|
|
||||||
|
Object subclass: #TestCase
|
||||||
|
instanceVariableNames: 'testSelector'!
|
||||||
|
|
||||||
|
!TestCase methodsFor: 'access'!
|
||||||
|
testSelector ^ testSelector!
|
||||||
|
testSelector: aSym testSelector := aSym. ^ self! !
|
||||||
|
|
||||||
|
!TestCase methodsFor: 'fixture'!
|
||||||
|
setUp ^ self!
|
||||||
|
tearDown ^ self! !
|
||||||
|
|
||||||
|
!TestCase methodsFor: 'asserts'!
|
||||||
|
assert: aBoolean
|
||||||
|
aBoolean ifFalse: [TestFailure signal: 'assertion failed'].
|
||||||
|
^ self!
|
||||||
|
|
||||||
|
assert: aBoolean description: aString
|
||||||
|
aBoolean ifFalse: [TestFailure signal: aString].
|
||||||
|
^ self!
|
||||||
|
|
||||||
|
assert: actual equals: expected
|
||||||
|
actual = expected ifFalse: [
|
||||||
|
TestFailure signal: 'expected ' , expected printString
|
||||||
|
, ' but got ' , actual printString].
|
||||||
|
^ self!
|
||||||
|
|
||||||
|
deny: aBoolean
|
||||||
|
aBoolean ifTrue: [TestFailure signal: 'denial failed'].
|
||||||
|
^ self!
|
||||||
|
|
||||||
|
should: aBlock raise: anExceptionClass
|
||||||
|
| raised |
|
||||||
|
raised := false.
|
||||||
|
[aBlock value] on: anExceptionClass do: [:e | raised := true].
|
||||||
|
raised ifFalse: [
|
||||||
|
TestFailure signal: 'expected exception ' , anExceptionClass name
|
||||||
|
, ' was not raised'].
|
||||||
|
^ self!
|
||||||
|
|
||||||
|
shouldnt: aBlock raise: anExceptionClass
|
||||||
|
| raised |
|
||||||
|
raised := false.
|
||||||
|
[aBlock value] on: anExceptionClass do: [:e | raised := true].
|
||||||
|
raised ifTrue: [
|
||||||
|
TestFailure signal: 'unexpected exception ' , anExceptionClass name].
|
||||||
|
^ self! !
|
||||||
|
|
||||||
|
!TestCase methodsFor: 'running'!
|
||||||
|
runCase
|
||||||
|
self setUp.
|
||||||
|
self perform: testSelector.
|
||||||
|
self tearDown.
|
||||||
|
^ self! !
|
||||||
|
|
||||||
|
!TestCase class methodsFor: 'instantiation'!
|
||||||
|
selector: aSym ^ self new testSelector: aSym!
|
||||||
|
|
||||||
|
suiteForAll: aSelectorArray
|
||||||
|
| suite |
|
||||||
|
suite := TestSuite new init.
|
||||||
|
suite name: self name.
|
||||||
|
aSelectorArray do: [:s | suite addTest: (self selector: s)].
|
||||||
|
^ suite! !
|
||||||
|
|
||||||
|
Object subclass: #TestResult
|
||||||
|
instanceVariableNames: 'passes failures errors'!
|
||||||
|
|
||||||
|
!TestResult methodsFor: 'init'!
|
||||||
|
init
|
||||||
|
passes := Array new: 0.
|
||||||
|
failures := Array new: 0.
|
||||||
|
errors := Array new: 0.
|
||||||
|
^ self! !
|
||||||
|
|
||||||
|
!TestResult methodsFor: 'access'!
|
||||||
|
passes ^ passes!
|
||||||
|
failures ^ failures!
|
||||||
|
errors ^ errors!
|
||||||
|
passCount ^ passes size!
|
||||||
|
failureCount ^ failures size!
|
||||||
|
errorCount ^ errors size!
|
||||||
|
totalCount ^ passes size + failures size + errors size!
|
||||||
|
|
||||||
|
addPass: aTest passes add: aTest. ^ self!
|
||||||
|
addFailure: aTest message: aMsg
|
||||||
|
| rec |
|
||||||
|
rec := Array new: 2.
|
||||||
|
rec at: 1 put: aTest. rec at: 2 put: aMsg.
|
||||||
|
failures add: rec.
|
||||||
|
^ self!
|
||||||
|
addError: aTest message: aMsg
|
||||||
|
| rec |
|
||||||
|
rec := Array new: 2.
|
||||||
|
rec at: 1 put: aTest. rec at: 2 put: aMsg.
|
||||||
|
errors add: rec.
|
||||||
|
^ self!
|
||||||
|
|
||||||
|
isEmpty ^ self totalCount = 0!
|
||||||
|
allPassed ^ (failures size + errors size) = 0!
|
||||||
|
|
||||||
|
summary
|
||||||
|
^ 'Tests: {1} Passed: {2} Failed: {3} Errors: {4}'
|
||||||
|
format: (Array
|
||||||
|
with: self totalCount printString
|
||||||
|
with: passes size printString
|
||||||
|
with: failures size printString
|
||||||
|
with: errors size printString)! !
|
||||||
|
|
||||||
|
Object subclass: #TestSuite
|
||||||
|
instanceVariableNames: 'tests name'!
|
||||||
|
|
||||||
|
!TestSuite methodsFor: 'init'!
|
||||||
|
init tests := Array new: 0. name := 'Suite'. ^ self!
|
||||||
|
name ^ name!
|
||||||
|
name: aString name := aString. ^ self! !
|
||||||
|
|
||||||
|
!TestSuite methodsFor: 'tests'!
|
||||||
|
tests ^ tests!
|
||||||
|
addTest: aTest tests add: aTest. ^ self!
|
||||||
|
addAll: aCollection aCollection do: [:t | self addTest: t]. ^ self!
|
||||||
|
size ^ tests size! !
|
||||||
|
|
||||||
|
!TestSuite methodsFor: 'running'!
|
||||||
|
run
|
||||||
|
| result |
|
||||||
|
result := TestResult new init.
|
||||||
|
tests do: [:t | self runTest: t result: result].
|
||||||
|
^ result!
|
||||||
|
|
||||||
|
runTest: aTest result: aResult
|
||||||
|
[aTest runCase. aResult addPass: aTest]
|
||||||
|
on: TestFailure do: [:e | aResult addFailure: aTest message: e messageText].
|
||||||
|
^ self! !")
|
||||||
|
|
||||||
|
(smalltalk-load st-sunit-source)
|
||||||
@@ -63,10 +63,12 @@ EPOCHS
|
|||||||
(epoch 4)
|
(epoch 4)
|
||||||
(load "lib/smalltalk/eval.sx")
|
(load "lib/smalltalk/eval.sx")
|
||||||
(epoch 5)
|
(epoch 5)
|
||||||
(load "lib/smalltalk/tests/tokenize.sx")
|
(load "lib/smalltalk/sunit.sx")
|
||||||
(epoch 6)
|
(epoch 6)
|
||||||
(load "$FILE")
|
(load "lib/smalltalk/tests/tokenize.sx")
|
||||||
(epoch 7)
|
(epoch 7)
|
||||||
|
(load "$FILE")
|
||||||
|
(epoch 8)
|
||||||
(eval "(list st-test-pass st-test-fail)")
|
(eval "(list st-test-pass st-test-fail)")
|
||||||
EPOCHS
|
EPOCHS
|
||||||
fi
|
fi
|
||||||
@@ -116,10 +118,12 @@ EPOCHS
|
|||||||
(epoch 4)
|
(epoch 4)
|
||||||
(load "lib/smalltalk/eval.sx")
|
(load "lib/smalltalk/eval.sx")
|
||||||
(epoch 5)
|
(epoch 5)
|
||||||
(load "lib/smalltalk/tests/tokenize.sx")
|
(load "lib/smalltalk/sunit.sx")
|
||||||
(epoch 6)
|
(epoch 6)
|
||||||
(load "$FILE")
|
(load "lib/smalltalk/tests/tokenize.sx")
|
||||||
(epoch 7)
|
(epoch 7)
|
||||||
|
(load "$FILE")
|
||||||
|
(epoch 8)
|
||||||
(eval "(map (fn (f) (get f :name)) st-test-fails)")
|
(eval "(map (fn (f) (get f :name)) st-test-fails)")
|
||||||
EPOCHS
|
EPOCHS
|
||||||
fi
|
fi
|
||||||
|
|||||||
198
lib/smalltalk/tests/sunit.sx
Normal file
198
lib/smalltalk/tests/sunit.sx
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
;; SUnit port tests. Loads `lib/smalltalk/sunit.sx` (which itself calls
|
||||||
|
;; smalltalk-load to install TestCase/TestSuite/TestResult/TestFailure)
|
||||||
|
;; and exercises the framework on small Smalltalk-defined cases.
|
||||||
|
|
||||||
|
(set! st-test-pass 0)
|
||||||
|
(set! st-test-fail 0)
|
||||||
|
(set! st-test-fails (list))
|
||||||
|
|
||||||
|
;; test.sh loads lib/smalltalk/sunit.sx for us BEFORE this file runs
|
||||||
|
;; (nested SX loads do not propagate top-level forms reliably, so the
|
||||||
|
;; bootstrap chain is concentrated in test.sh). The SUnit classes are
|
||||||
|
;; already present in the class table at this point.
|
||||||
|
|
||||||
|
(define ev (fn (src) (smalltalk-eval src)))
|
||||||
|
(define evp (fn (src) (smalltalk-eval-program src)))
|
||||||
|
|
||||||
|
;; ── 1. Classes installed ──
|
||||||
|
(st-test "TestCase exists" (st-class-exists? "TestCase") true)
|
||||||
|
(st-test "TestSuite exists" (st-class-exists? "TestSuite") true)
|
||||||
|
(st-test "TestResult exists" (st-class-exists? "TestResult") true)
|
||||||
|
(st-test "TestFailure < Error"
|
||||||
|
(st-class-inherits-from? "TestFailure" "Error") true)
|
||||||
|
|
||||||
|
;; ── 2. A subclass with one passing test runs cleanly ──
|
||||||
|
(smalltalk-load
|
||||||
|
"TestCase subclass: #PassingCase
|
||||||
|
instanceVariableNames: ''!
|
||||||
|
|
||||||
|
!PassingCase methodsFor: 'tests'!
|
||||||
|
testOnePlusOne self assert: 1 + 1 = 2! !")
|
||||||
|
|
||||||
|
(st-test "passing test runs and counts as pass"
|
||||||
|
(evp
|
||||||
|
"| suite r |
|
||||||
|
suite := PassingCase suiteForAll: #(#testOnePlusOne).
|
||||||
|
r := suite run.
|
||||||
|
^ r passCount")
|
||||||
|
1)
|
||||||
|
|
||||||
|
(st-test "passing test has no failures"
|
||||||
|
(evp
|
||||||
|
"| suite r |
|
||||||
|
suite := PassingCase suiteForAll: #(#testOnePlusOne).
|
||||||
|
r := suite run.
|
||||||
|
^ r failureCount")
|
||||||
|
0)
|
||||||
|
|
||||||
|
;; ── 3. A subclass with a failing assert: increments failures ──
|
||||||
|
(smalltalk-load
|
||||||
|
"TestCase subclass: #FailingCase
|
||||||
|
instanceVariableNames: ''!
|
||||||
|
|
||||||
|
!FailingCase methodsFor: 'tests'!
|
||||||
|
testFalse self assert: false!
|
||||||
|
testEquals self assert: 1 + 1 equals: 3! !")
|
||||||
|
|
||||||
|
(st-test "assert: false bumps failureCount"
|
||||||
|
(evp
|
||||||
|
"| suite r |
|
||||||
|
suite := FailingCase suiteForAll: #(#testFalse).
|
||||||
|
r := suite run.
|
||||||
|
^ r failureCount")
|
||||||
|
1)
|
||||||
|
|
||||||
|
(st-test "assert:equals: with mismatch fails"
|
||||||
|
(evp
|
||||||
|
"| suite r |
|
||||||
|
suite := FailingCase suiteForAll: #(#testEquals).
|
||||||
|
r := suite run.
|
||||||
|
^ r failureCount")
|
||||||
|
1)
|
||||||
|
|
||||||
|
(st-test "failure messageText captured"
|
||||||
|
(evp
|
||||||
|
"| suite r rec |
|
||||||
|
suite := FailingCase suiteForAll: #(#testEquals).
|
||||||
|
r := suite run.
|
||||||
|
rec := r failures at: 1.
|
||||||
|
^ rec at: 2")
|
||||||
|
"expected 3 but got 2")
|
||||||
|
|
||||||
|
;; ── 4. Mixed pass/fail counts add up ──
|
||||||
|
(smalltalk-load
|
||||||
|
"TestCase subclass: #MixedCase
|
||||||
|
instanceVariableNames: ''!
|
||||||
|
|
||||||
|
!MixedCase methodsFor: 'tests'!
|
||||||
|
testGood self assert: true!
|
||||||
|
testBad self assert: false!
|
||||||
|
testAlsoGood self assert: 2 > 1! !")
|
||||||
|
|
||||||
|
(st-test "mixed suite — totalCount"
|
||||||
|
(evp
|
||||||
|
"| s r |
|
||||||
|
s := MixedCase suiteForAll: #(#testGood #testBad #testAlsoGood).
|
||||||
|
r := s run.
|
||||||
|
^ r totalCount")
|
||||||
|
3)
|
||||||
|
|
||||||
|
(st-test "mixed suite — passCount"
|
||||||
|
(evp
|
||||||
|
"| s r |
|
||||||
|
s := MixedCase suiteForAll: #(#testGood #testBad #testAlsoGood).
|
||||||
|
r := s run.
|
||||||
|
^ r passCount")
|
||||||
|
2)
|
||||||
|
|
||||||
|
(st-test "mixed suite — failureCount"
|
||||||
|
(evp
|
||||||
|
"| s r |
|
||||||
|
s := MixedCase suiteForAll: #(#testGood #testBad #testAlsoGood).
|
||||||
|
r := s run.
|
||||||
|
^ r failureCount")
|
||||||
|
1)
|
||||||
|
|
||||||
|
(st-test "allPassed false on mix"
|
||||||
|
(evp
|
||||||
|
"| s r |
|
||||||
|
s := MixedCase suiteForAll: #(#testGood #testBad #testAlsoGood).
|
||||||
|
r := s run.
|
||||||
|
^ r allPassed")
|
||||||
|
false)
|
||||||
|
|
||||||
|
(st-test "allPassed true with only passes"
|
||||||
|
(evp
|
||||||
|
"| s r |
|
||||||
|
s := MixedCase suiteForAll: #(#testGood #testAlsoGood).
|
||||||
|
r := s run.
|
||||||
|
^ r allPassed")
|
||||||
|
true)
|
||||||
|
|
||||||
|
;; ── 5. setUp / tearDown ──
|
||||||
|
(smalltalk-load
|
||||||
|
"TestCase subclass: #FixtureCase
|
||||||
|
instanceVariableNames: 'value'!
|
||||||
|
|
||||||
|
!FixtureCase methodsFor: 'fixture'!
|
||||||
|
setUp value := 42. ^ self!
|
||||||
|
tearDown ^ self! !
|
||||||
|
|
||||||
|
!FixtureCase methodsFor: 'tests'!
|
||||||
|
testValueIs42 self assert: value = 42! !")
|
||||||
|
|
||||||
|
(st-test "setUp ran before test"
|
||||||
|
(evp
|
||||||
|
"| s r |
|
||||||
|
s := FixtureCase suiteForAll: #(#testValueIs42).
|
||||||
|
r := s run.
|
||||||
|
^ r passCount")
|
||||||
|
1)
|
||||||
|
|
||||||
|
;; ── 6. should:raise: and shouldnt:raise: ──
|
||||||
|
(smalltalk-load
|
||||||
|
"TestCase subclass: #RaiseCase
|
||||||
|
instanceVariableNames: ''!
|
||||||
|
|
||||||
|
!RaiseCase methodsFor: 'tests'!
|
||||||
|
testShouldRaise
|
||||||
|
self should: [Error signal: 'boom'] raise: Error!
|
||||||
|
|
||||||
|
testShouldRaiseFails
|
||||||
|
self should: [42] raise: Error!
|
||||||
|
|
||||||
|
testShouldntRaise
|
||||||
|
self shouldnt: [42] raise: Error! !")
|
||||||
|
|
||||||
|
(st-test "should:raise: catches matching"
|
||||||
|
(evp
|
||||||
|
"| r |
|
||||||
|
r := (RaiseCase suiteForAll: #(#testShouldRaise)) run.
|
||||||
|
^ r passCount") 1)
|
||||||
|
|
||||||
|
(st-test "should:raise: fails when no exception"
|
||||||
|
(evp
|
||||||
|
"| r |
|
||||||
|
r := (RaiseCase suiteForAll: #(#testShouldRaiseFails)) run.
|
||||||
|
^ r failureCount") 1)
|
||||||
|
|
||||||
|
(st-test "shouldnt:raise: passes when nothing thrown"
|
||||||
|
(evp
|
||||||
|
"| r |
|
||||||
|
r := (RaiseCase suiteForAll: #(#testShouldntRaise)) run.
|
||||||
|
^ r passCount") 1)
|
||||||
|
|
||||||
|
;; ── 7. summary string uses format: ──
|
||||||
|
(st-test "summary contains pass count"
|
||||||
|
(let
|
||||||
|
((s (evp
|
||||||
|
"| s r |
|
||||||
|
s := MixedCase suiteForAll: #(#testGood #testBad).
|
||||||
|
r := s run.
|
||||||
|
^ r summary")))
|
||||||
|
(cond
|
||||||
|
((not (string? s)) false)
|
||||||
|
(else (> (len s) 0))))
|
||||||
|
true)
|
||||||
|
|
||||||
|
(list st-test-pass st-test-fail)
|
||||||
@@ -94,7 +94,7 @@ Core mapping:
|
|||||||
- [x] `String>>format:`, `printOn:` for everything. `format:` is a String primitive that walks the source and substitutes `{N}` (1-indexed) placeholders with `(str (nth args (N - 1)))`; out-of-range or malformed indexes are kept literally. `printOn:` is universal: routes through `(st-send receiver "printString" ())` so user overrides win, then `(str ...)` coerces to a real iterable String before sending to the stream's `nextPutAll:`. `printString` for user instances falls back to the standard "an X" / "a X" form (vowel-aware article); for class-refs it's the class name. 18 tests in `lib/smalltalk/tests/printing.sx`. Phase 5 complete.
|
- [x] `String>>format:`, `printOn:` for everything. `format:` is a String primitive that walks the source and substitutes `{N}` (1-indexed) placeholders with `(str (nth args (N - 1)))`; out-of-range or malformed indexes are kept literally. `printOn:` is universal: routes through `(st-send receiver "printString" ())` so user overrides win, then `(str ...)` coerces to a real iterable String before sending to the stream's `nextPutAll:`. `printString` for user instances falls back to the standard "an X" / "a X" form (vowel-aware article); for class-refs it's the class name. 18 tests in `lib/smalltalk/tests/printing.sx`. Phase 5 complete.
|
||||||
|
|
||||||
### Phase 6 — SUnit + corpus to 200+
|
### Phase 6 — SUnit + corpus to 200+
|
||||||
- [ ] Port SUnit (TestCase, TestSuite, TestResult) — written in SX-Smalltalk, runs in itself
|
- [x] Port SUnit (`lib/smalltalk/sunit.sx`). Written in Smalltalk source via `smalltalk-load`. Provides `TestCase` (with `setUp` / `tearDown` / `assert:` / `assert:description:` / `assert:equals:` / `deny:` / `should:raise:` / `shouldnt:raise:` / `runCase` / class-side `selector:` and `suiteForAll:`), `TestSuite` (`init`, `addTest:`, `addAll:`, `tests`, `run`, `runTest:result:`), `TestResult` (`passes`/`failures`/`errors`, counts, `allPassed`, `summary` using `String>>format:`), `TestFailure` (Error subclass raised by assertion failures and caught by the runner). 19 tests in `lib/smalltalk/tests/sunit.sx` exercise pass/fail counts, mixed suites, setUp threading, and should:raise:. test.sh now loads `lib/smalltalk/sunit.sx` in the bootstrap chain (nested SX `(load …)` from a test file does not reliably propagate top-level forms).
|
||||||
- [ ] Vendor a slice of Pharo `Kernel-Tests` and `Collections-Tests`
|
- [ ] Vendor a slice of Pharo `Kernel-Tests` and `Collections-Tests`
|
||||||
- [ ] Drive the scoreboard up: aim for 200+ green tests
|
- [ ] Drive the scoreboard up: aim for 200+ green tests
|
||||||
- [ ] Stretch: ANSI Smalltalk validator subset
|
- [ ] Stretch: ANSI Smalltalk validator subset
|
||||||
@@ -108,6 +108,7 @@ Core mapping:
|
|||||||
|
|
||||||
_Newest first. Agent appends on every commit._
|
_Newest first. Agent appends on every commit._
|
||||||
|
|
||||||
|
- 2026-04-25: SUnit port (`lib/smalltalk/sunit.sx`, `lib/smalltalk/tests/sunit.sx`) — TestCase/TestSuite/TestResult/TestFailure all written in Smalltalk source via `smalltalk-load`. Full assert family + should:raise: + setUp/tearDown threading. 19 tests verify the framework. test.sh now bootstraps SUnit alongside runtime/eval. 660/660 total.
|
||||||
- 2026-04-25: String>>format: + universal printOn: + 18 tests (`lib/smalltalk/tests/printing.sx`). `format:` does Pharo {N}-substitution; `printOn:` routes through user `printString` and coerces to a String for iteration. Phase 5 complete. 638/638 total.
|
- 2026-04-25: String>>format: + universal printOn: + 18 tests (`lib/smalltalk/tests/printing.sx`). `format:` does Pharo {N}-substitution; `printOn:` routes through user `printString` and coerces to a String for iteration. Phase 5 complete. 638/638 total.
|
||||||
- 2026-04-25: Number tower + Fraction class + 47 tests (`lib/smalltalk/tests/numbers.sx`). 14 new Number primitives (floor/ceiling/truncated/rounded/sqrt/squared/raisedTo:/factorial/even/odd/gcd:/lcm:/isInteger/isFloat). Fraction with normalisation + arithmetic + comparisons + asFloat. 620/620 total.
|
- 2026-04-25: Number tower + Fraction class + 47 tests (`lib/smalltalk/tests/numbers.sx`). 14 new Number primitives (floor/ceiling/truncated/rounded/sqrt/squared/raisedTo:/factorial/even/odd/gcd:/lcm:/isInteger/isFloat). Fraction with normalisation + arithmetic + comparisons + asFloat. 620/620 total.
|
||||||
- 2026-04-25: Stream hierarchy + 21 tests (`lib/smalltalk/tests/streams.sx`). ReadStream / WriteStream / ReadWriteStream as user classes; class-side `on:`; ReadStream-on-String yields characters. Bumped `test.sh` per-file timeout 60s → 180s — heavier bootstrap pushed `programs.sx` past 60s. 573/573 total.
|
- 2026-04-25: Stream hierarchy + 21 tests (`lib/smalltalk/tests/streams.sx`). ReadStream / WriteStream / ReadWriteStream as user classes; class-side `on:`; ReadStream-on-String yields characters. Bumped `test.sh` per-file timeout 60s → 180s — heavier bootstrap pushed `programs.sx` past 60s. 573/573 total.
|
||||||
|
|||||||
Reference in New Issue
Block a user