SUnit.st 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. Object subclass: #TestCase
  2. instanceVariableNames: 'testedClass'
  3. category: 'SUnit'!
  4. !TestCase methodsFor: 'accessing'!
  5. testedClass
  6. ^testedClass
  7. !
  8. testedClass: aClass
  9. testedClass := aClass
  10. ! !
  11. !TestCase methodsFor: 'private'!
  12. cleanUpInstanceVariables
  13. self class instanceVariableNames do: [ :name |
  14. name = 'testSelector' ifFalse: [
  15. self instVarAt: name put: nil ]]
  16. !
  17. signalFailure: aString
  18. TestFailure new
  19. messageText: aString;
  20. signal
  21. ! !
  22. !TestCase methodsFor: 'running'!
  23. setUp
  24. !
  25. tearDown
  26. !
  27. methods
  28. ^self class methodDictionary keys select: [:each | each match: '^test']
  29. !
  30. runCaseFor: aTestResult
  31. [self setUp.
  32. self performTestFor: aTestResult]
  33. on: Error
  34. do: [:ex |
  35. self tearDown.
  36. self cleanUpInstanceVariables.
  37. ex signal].
  38. self tearDown.
  39. self cleanUpInstanceVariables
  40. !
  41. performTestFor: aResult
  42. self methods do: [:each |
  43. [[self perform: each]
  44. on: TestFailure do: [:ex | aResult addFailure: self class name, '>>', each, ': ', ex messageText]]
  45. on: Error do: [:ex | aResult addError: self class name, '>>', each, ': ', ex messageText].
  46. aResult increaseRuns]
  47. ! !
  48. !TestCase methodsFor: 'testing'!
  49. assert: aBoolean
  50. self assert: aBoolean description: 'Assertion failed'
  51. !
  52. deny: aBoolean
  53. self assert: aBoolean not
  54. !
  55. assert: expected equals: actual
  56. ^ self assert: (expected = actual) description: 'Expected: ', expected asString, ' but was: ', actual asString
  57. !
  58. assert: aBoolean description: aString
  59. aBoolean ifFalse: [self signalFailure: aString]
  60. ! !
  61. TestCase subclass: #ExampleTest
  62. instanceVariableNames: 'test'
  63. category: 'SUnit'!
  64. !ExampleTest methodsFor: 'not yet classified'!
  65. testFailure
  66. self deny: true
  67. !
  68. testPasses
  69. 100000 timesRepeat: [self assert: 1 + 1 = 2]
  70. !
  71. testError
  72. self assert: 1 foo
  73. ! !
  74. TabWidget subclass: #ProgressBar
  75. instanceVariableNames: 'percent progressDiv div'
  76. category: 'SUnit'!
  77. !ProgressBar methodsFor: 'accessing'!
  78. percent
  79. ^percent ifNil: [0]
  80. !
  81. percent: aNumber
  82. percent := aNumber
  83. ! !
  84. !ProgressBar methodsFor: 'rendering'!
  85. renderOn: html
  86. div := html div
  87. class: 'progress_bar';
  88. yourself.
  89. self renderProgressBar
  90. !
  91. renderProgressBar
  92. div contents: [:html |
  93. html div
  94. class: 'progress';
  95. style: 'width:', self percent asString, '%']
  96. ! !
  97. !ProgressBar methodsFor: 'updating'!
  98. updatePercent: aNumber
  99. self percent: aNumber.
  100. self renderProgressBar
  101. ! !
  102. Error subclass: #TestFailure
  103. instanceVariableNames: ''
  104. category: 'SUnit'!
  105. TabWidget subclass: #TestRunner
  106. instanceVariableNames: 'selectedCategories categoriesList selectedClasses classesList selectedMethods progressBar methodsList result statusDiv'
  107. category: 'SUnit'!
  108. !TestRunner methodsFor: 'accessing'!
  109. label
  110. ^'[Test runner]'
  111. !
  112. categories
  113. | categories |
  114. categories := Array new.
  115. self allClasses do: [:each |
  116. (categories includes: each category) ifFalse: [
  117. categories add: each category]].
  118. ^categories sort
  119. !
  120. classes
  121. ^(self allClasses
  122. select: [:each | self selectedCategories includes: each category])
  123. sort: [:a :b | a name > b name]
  124. !
  125. selectedCategories
  126. ^selectedCategories ifNil: [selectedCategories := Array new]
  127. !
  128. allClasses
  129. ^TestCase allSubclasses
  130. !
  131. selectedClasses
  132. ^selectedClasses ifNil: [selectedClasses := Array new]
  133. !
  134. progressBar
  135. ^progressBar ifNil: [progressBar := ProgressBar new]
  136. !
  137. selectedMethods
  138. ^selectedMethods ifNil: [self selectedClasses collect: [:each |
  139. each methodDictionary keys select: [:key | key beginsWith: 'test' ]]]
  140. !
  141. statusInfo
  142. ^self printTotal, self printPasses, self printErrors, self printFailures
  143. !
  144. result
  145. ^result
  146. !
  147. failedMethods
  148. self result failures collect: [:each |
  149. html li
  150. class: 'failures';
  151. with: each]
  152. ! !
  153. !TestRunner methodsFor: 'actions'!
  154. selectAllCategories
  155. self categories do: [:each |
  156. (selectedCategories includes: each) ifFalse: [
  157. self selectedCategories add: each]].
  158. self
  159. updateCategoriesList;
  160. updateClassesList
  161. !
  162. toggleCategory: aCategory
  163. (self isSelectedCategory: aCategory)
  164. ifFalse: [selectedCategories add: aCategory]
  165. ifTrue: [selectedCategories remove: aCategory].
  166. self
  167. updateCategoriesList;
  168. updateClassesList
  169. !
  170. toggleClass: aClass
  171. (self isSelectedClass: aClass)
  172. ifFalse: [selectedClasses add: aClass]
  173. ifTrue: [selectedClasses remove: aClass].
  174. self
  175. updateClassesList
  176. !
  177. selectAllClasses
  178. self classes do: [:each |
  179. (selectedClasses includes: each) ifFalse: [
  180. self selectedClasses add: each]].
  181. self
  182. updateCategoriesList;
  183. updateClassesList
  184. !
  185. run: aCollection
  186. result := TestResult new.
  187. self
  188. updateStatusDiv;
  189. updateMethodsList.
  190. self progressBar updatePercent: 0.
  191. result total: (aCollection inject: 0 into: [:acc :each | acc + each methods size]).
  192. aCollection do: [:each |
  193. [each runCaseFor: result.
  194. self progressBar updatePercent: result runs / result total * 100.
  195. self updateStatusDiv.
  196. self updateMethodsList] valueWithTimeout: 100].
  197. ! !
  198. !TestRunner methodsFor: 'initialization'!
  199. initialize
  200. super initialize.
  201. result := TestResult new
  202. ! !
  203. !TestRunner methodsFor: 'printing'!
  204. printErrors
  205. ^self result errors size asString , ' errors, '
  206. !
  207. printFailures
  208. ^self result failures size asString, ' failures'
  209. !
  210. printPasses
  211. ^(((self result total) - (self result errors size + (self result failures size))) asString) , ' passes, '
  212. !
  213. printTotal
  214. ^self result total asString, ' runs, '
  215. ! !
  216. !TestRunner methodsFor: 'rendering'!
  217. renderBoxOn: html
  218. self
  219. renderCategoriesOn: html;
  220. renderClassesOn: html;
  221. renderResultsOn: html
  222. !
  223. renderButtonsOn: html
  224. html button
  225. with: 'Run selected';
  226. onClick: [self run: (self selectedClasses collect: [:each | each new])]
  227. !
  228. renderCategoriesOn: html
  229. categoriesList := html ul class: 'jt_column sunit categories'.
  230. self updateCategoriesList
  231. !
  232. renderClassesOn: html
  233. classesList := html ul class: 'jt_column sunit classes'.
  234. self updateClassesList
  235. !
  236. renderResultsOn: html
  237. statusDiv := html div.
  238. html with: self progressBar.
  239. methodsList := html ul class: 'jt_column sunit methods'.
  240. self updateMethodsList.
  241. self updateStatusDiv
  242. !
  243. renderFailuresOn: html
  244. self result failures do: [:each |
  245. html li
  246. class: 'failures';
  247. with: each]
  248. !
  249. renderErrorsOn: html
  250. self result errors do: [:each |
  251. html li
  252. class: 'errors';
  253. with: each]
  254. ! !
  255. !TestRunner methodsFor: 'testing'!
  256. canBeClosed
  257. ^true
  258. !
  259. isSelectedClass: aClass
  260. ^(self selectedClasses includes: aClass)
  261. !
  262. isSelectedCategory: aCategory
  263. ^(self selectedCategories includes: aCategory)
  264. ! !
  265. !TestRunner methodsFor: 'updating'!
  266. updateCategoriesList
  267. categoriesList contents: [:html |
  268. html li
  269. class: 'all';
  270. with: 'All';
  271. onClick: [self selectAllCategories].
  272. self categories do: [:each || li |
  273. li := html li.
  274. (self selectedCategories includes: each) ifTrue: [
  275. li class: 'selected'].
  276. li
  277. with: each;
  278. onClick: [self toggleCategory: each]]]
  279. !
  280. updateClassesList
  281. classesList contents: [:html |
  282. (self selectedCategories isEmpty) ifFalse: [
  283. html li
  284. class: 'all';
  285. with: 'All';
  286. onClick: [self selectAllClasses]].
  287. self classes do: [:each || li |
  288. li := html li.
  289. (self selectedClasses includes: each) ifTrue: [
  290. li class: 'selected'].
  291. li
  292. with: each name;
  293. onClick: [self toggleClass: each]]]
  294. !
  295. updateMethodsList
  296. methodsList contents: [:html |
  297. self renderFailuresOn: html.
  298. self renderErrorsOn: html]
  299. !
  300. updateStatusDiv
  301. statusDiv class: 'sunit status ', result status.
  302. statusDiv contents: [:html |
  303. html span with: self statusInfo]
  304. ! !
  305. Object subclass: #TestResult
  306. instanceVariableNames: 'timestamp runs errors failures total'
  307. category: 'SUnit'!
  308. !TestResult methodsFor: 'accessing'!
  309. timestamp
  310. ^timestamp
  311. !
  312. errors
  313. ^errors
  314. !
  315. failures
  316. ^failures
  317. !
  318. total
  319. ^total
  320. !
  321. total: aNumber
  322. total := aNumber
  323. !
  324. addError: anError
  325. self errors add: anError
  326. !
  327. addFailure: aFailure
  328. self failures add: aFailure
  329. !
  330. runs
  331. ^runs
  332. !
  333. increaseRuns
  334. runs := runs + 1
  335. !
  336. status
  337. ^self errors isEmpty
  338. ifTrue: [
  339. self failures isEmpty
  340. ifTrue: ['success']
  341. ifFalse: ['failure']]
  342. ifFalse: ['error']
  343. ! !
  344. !TestResult methodsFor: 'initialization'!
  345. initialize
  346. super initialize.
  347. timestamp := Date now.
  348. runs := 0.
  349. errors := Array new.
  350. failures := Array new.
  351. total := 0
  352. ! !
  353. TestCase subclass: #ExampleTest2
  354. instanceVariableNames: ''
  355. category: 'SUnit'!
  356. !ExampleTest2 methodsFor: 'not yet classified'!
  357. testPasses
  358. 100000 timesRepeat: [self assert: 1 + 1 = 2]
  359. ! !
  360. TestCase subclass: #ExampleTest3
  361. instanceVariableNames: ''
  362. category: 'SUnit'!
  363. !ExampleTest3 methodsFor: 'not yet classified'!
  364. testPasses
  365. 100000 timesRepeat: [self assert: 1 + 1 = 2]
  366. ! !