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