Helios-SUnit.st 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. Smalltalk createPackage: 'Helios-SUnit'!
  2. HLToolListWidget subclass: #HLMultiSelectToolListWidget
  3. instanceVariableNames: ''
  4. package: 'Helios-SUnit'!
  5. !HLMultiSelectToolListWidget commentStamp!
  6. This is a list that handles multiple selection!
  7. !HLMultiSelectToolListWidget methodsFor: 'accessing'!
  8. activeItemCssClass
  9. ^'selector'
  10. !
  11. listCssClass
  12. ^'nav nav-multiselect nav-pills nav-stacked'
  13. !
  14. listCssClassForItem: anObject
  15. ^(super listCssClassForItem: anObject), ((self isSelected: anObject)
  16. ifTrue: [' active']
  17. ifFalse: ['']).
  18. ! !
  19. !HLMultiSelectToolListWidget methodsFor: 'actions'!
  20. select: anObject
  21. self subclassResponsibility
  22. !
  23. toggleListItem: aListItem
  24. | item |
  25. (aListItem get: 0) ifNil: [ ^ self ].
  26. "Find item"
  27. item := aListItem data: 'item'.
  28. self toggleSelection: item
  29. !
  30. toggleSelection: anObject
  31. (self isSelected: anObject)
  32. ifTrue: [ self unselect: anObject ]
  33. ifFalse: [self select: anObject ]
  34. !
  35. unselect: anObject
  36. self subclassResponsibility
  37. ! !
  38. !HLMultiSelectToolListWidget methodsFor: 'rendering'!
  39. reselectItem: anObject
  40. anObject ifNil: [^self].
  41. self toggleSelection: anObject
  42. ! !
  43. !HLMultiSelectToolListWidget methodsFor: 'testing'!
  44. isSelected: anObject
  45. self subclassResponsibility
  46. ! !
  47. HLMultiSelectToolListWidget subclass: #HLSUnitClassesListWidget
  48. instanceVariableNames: ''
  49. package: 'Helios-SUnit'!
  50. !HLSUnitClassesListWidget commentStamp!
  51. I display a list of classes (subclasses of `TestCase`).!
  52. !HLSUnitClassesListWidget methodsFor: 'accessing'!
  53. cssClassForItem: aClass
  54. ^ aClass theNonMetaClass heliosClass
  55. !
  56. items
  57. ^ items ifNil: [ self initializeItems ]
  58. !
  59. label
  60. ^ 'Classes'
  61. ! !
  62. !HLSUnitClassesListWidget methodsFor: 'actions'!
  63. observeModel
  64. self model announcer
  65. on: HLPackageSelected
  66. send: #onPackageSelected:
  67. to: self;
  68. on: HLPackageUnselected
  69. send: #onPackageUnselected:
  70. to: self;
  71. on: HLClassSelected
  72. send: #onClassSelected:
  73. to: self;
  74. on: HLClassUnselected
  75. send: #onClassUnselected:
  76. to: self.
  77. !
  78. observeSystem
  79. self model systemAnnouncer
  80. on: ClassAdded
  81. send: #onClassAdded:
  82. to: self.
  83. !
  84. select: anObject
  85. model selectClass: anObject
  86. !
  87. unselect: anObject
  88. model unselectClass: anObject
  89. ! !
  90. !HLSUnitClassesListWidget methodsFor: 'initialization'!
  91. initializeItems
  92. ^items := model testClasses
  93. ! !
  94. !HLSUnitClassesListWidget methodsFor: 'reactions'!
  95. onClassAdded: anAnnouncement
  96. (self model selectedPackages includes: anAnnouncement theClass package)
  97. ifTrue: [
  98. self
  99. initializeItems;
  100. refresh ]
  101. !
  102. onClassSelected: anAnnouncement
  103. | listItem |
  104. listItem := self findListItemFor: anAnnouncement item.
  105. listItem addClass: 'active'.
  106. !
  107. onClassUnselected: anAnnouncement
  108. | listItem |
  109. listItem := self findListItemFor: anAnnouncement item.
  110. listItem removeClass: 'active'.
  111. !
  112. onPackageSelected: anAnnouncement
  113. self initializeItems;
  114. refresh
  115. !
  116. onPackageUnselected: anAnnouncement
  117. self initializeItems;
  118. refresh
  119. ! !
  120. !HLSUnitClassesListWidget methodsFor: 'rendering'!
  121. renderItemLabel: aClass on: html
  122. html with: aClass name
  123. ! !
  124. !HLSUnitClassesListWidget methodsFor: 'testing'!
  125. isSelected: anObject
  126. ^model selectedClasses includes: anObject
  127. ! !
  128. HLMultiSelectToolListWidget subclass: #HLSUnitPackagesListWidget
  129. instanceVariableNames: ''
  130. package: 'Helios-SUnit'!
  131. !HLSUnitPackagesListWidget commentStamp!
  132. I display a list of packages for which unit tests are associated (packages containing subclasses of `TestCase`).!
  133. !HLSUnitPackagesListWidget methodsFor: 'accessing'!
  134. cssClassForItem: anItem
  135. ^ anItem isDirty
  136. ifTrue: [ 'package_dirty' ]
  137. ifFalse: [ 'package' ]
  138. !
  139. items
  140. ^ items ifNil: [ self initializeItems ]
  141. !
  142. label
  143. ^ 'Packages'
  144. ! !
  145. !HLSUnitPackagesListWidget methodsFor: 'actions'!
  146. observeModel
  147. self model announcer
  148. on: HLPackageSelected
  149. send: #onPackageSelected:
  150. to: self;
  151. on: HLPackageUnselected
  152. send: #onPackageUnselected:
  153. to: self
  154. !
  155. observeSystem
  156. self model systemAnnouncer
  157. on: ClassAdded
  158. send: #onClassAdded:
  159. to: self.
  160. !
  161. select: anObject
  162. model selectPackage: anObject
  163. !
  164. unselect: anObject
  165. model unselectPackage: anObject
  166. ! !
  167. !HLSUnitPackagesListWidget methodsFor: 'initialization'!
  168. initializeItems
  169. ^items := model testPackages
  170. sort: [:a :b | a name < b name]
  171. ! !
  172. !HLSUnitPackagesListWidget methodsFor: 'reactions'!
  173. onClassAdded: anAnnouncement
  174. ((self items includes: anAnnouncement theClass package) not and: [anAnnouncement theClass package isTestPackage])
  175. ifTrue: [
  176. self
  177. initializeItems;
  178. refresh ]
  179. !
  180. onPackageSelected: anAnnouncement
  181. | listItem |
  182. listItem := self findListItemFor: anAnnouncement item.
  183. listItem addClass: 'active'.
  184. !
  185. onPackageUnselected: anAnnouncement
  186. | listItem |
  187. listItem := self findListItemFor: anAnnouncement item.
  188. listItem removeClass: 'active'.
  189. ! !
  190. !HLSUnitPackagesListWidget methodsFor: 'rendering'!
  191. renderButtonsOn: html
  192. html button
  193. with: 'Run Tests';
  194. onClick: [ self model runTests ]
  195. !
  196. renderItemLabel: aPackage on: html
  197. html with: aPackage name
  198. ! !
  199. !HLSUnitPackagesListWidget methodsFor: 'testing'!
  200. isSelected: anObject
  201. ^model selectedPackages includes: anObject
  202. ! !
  203. HLWidget subclass: #HLSUnit
  204. instanceVariableNames: 'model packagesListWidget classesListWidget resultWidget failuresWidget errorsWidget'
  205. package: 'Helios-SUnit'!
  206. !HLSUnit commentStamp!
  207. I am the main widget for running unit tests in Helios.
  208. I provide the ability to select set of tests to run per package, and a detailed result log with passed tests, failed tests and errors.!
  209. !HLSUnit methodsFor: 'accessing'!
  210. model
  211. ^ model ifNil: [ model := HLSUnitModel new ]
  212. !
  213. resultSection
  214. ^HLHorizontalSplitter
  215. with: self resultWidget
  216. with: (HLHorizontalSplitter
  217. with: self failuresWidget
  218. with: self errorsWidget)
  219. ! !
  220. !HLSUnit methodsFor: 'keybindings'!
  221. registerBindingsOn: aBindingGroup
  222. HLToolCommand
  223. registerConcreteClassesOn: aBindingGroup
  224. for: self model
  225. ! !
  226. !HLSUnit methodsFor: 'rendering'!
  227. renderContentOn: html
  228. | resultSection |
  229. html with: (HLContainer with: (
  230. HLVerticalSplitter
  231. with: (HLVerticalSplitter
  232. with: self packagesListWidget
  233. with: self classesListWidget)
  234. with: (resultSection := self resultSection))).
  235. [resultSection resize: 0] valueWithTimeout: 100.
  236. self packagesListWidget focus
  237. ! !
  238. !HLSUnit methodsFor: 'widgets'!
  239. classesListWidget
  240. ^ classesListWidget ifNil: [
  241. classesListWidget := HLSUnitClassesListWidget on: self model.
  242. classesListWidget next: self failuresWidget ]
  243. !
  244. errorsWidget
  245. ^ errorsWidget ifNil: [errorsWidget := HLSUnitErrorsListWidget on: self model]
  246. !
  247. failuresWidget
  248. ^ failuresWidget ifNil: [
  249. failuresWidget := HLSUnitFailuresListWidget on: self model.
  250. failuresWidget next: self errorsWidget]
  251. !
  252. packagesListWidget
  253. ^ packagesListWidget ifNil: [
  254. packagesListWidget := HLSUnitPackagesListWidget on: self model.
  255. packagesListWidget next: self classesListWidget]
  256. !
  257. resultWidget
  258. ^ resultWidget ifNil: [
  259. resultWidget := HLSUnitResults new
  260. model: self model;
  261. yourself]
  262. ! !
  263. !HLSUnit class methodsFor: 'accessing'!
  264. tabClass
  265. ^ 'sunit'
  266. !
  267. tabLabel
  268. ^ 'SUnit'
  269. !
  270. tabPriority
  271. ^ 1000
  272. ! !
  273. !HLSUnit class methodsFor: 'testing'!
  274. canBeOpenAsTab
  275. ^ true
  276. ! !
  277. HLModel subclass: #HLSUnitModel
  278. instanceVariableNames: 'selectedPackages selectedClasses testResult currentSuite'
  279. package: 'Helios-SUnit'!
  280. !HLSUnitModel commentStamp!
  281. I am the model for running unit tests in Helios.
  282. I provide the ability to select set of tests to run per package, and a detailed result log with passed tests, failed tests and errors.!
  283. !HLSUnitModel methodsFor: 'accessing'!
  284. currentSuite
  285. ^currentSuite
  286. !
  287. selectedClasses
  288. ^ (self unfilteredSelectedClasses) select: [:each |
  289. self selectedPackages includes: each package]
  290. !
  291. selectedPackages
  292. ^ selectedPackages ifNil: [ selectedPackages := Set new ]
  293. !
  294. testCases
  295. | testCases |
  296. testCases := #().
  297. self selectedClasses
  298. do: [ :each | testCases addAll: each buildSuite ].
  299. ^ testCases
  300. !
  301. testClasses
  302. "Answer all concrete subclasses of TestCase in selected packages"
  303. | stream |
  304. stream := Array new writeStream.
  305. self selectedPackages do: [ :package |
  306. stream nextPutAll: (package classes select: [ :each |
  307. (each includesBehavior: TestCase) and: [
  308. each isAbstract not ] ] ) ].
  309. ^ stream contents
  310. !
  311. testPackages
  312. "Answer all packages containing concrete subclasses of TestCase"
  313. ^ self environment packages
  314. select: [ :each | each isTestPackage ]
  315. !
  316. testResult
  317. ^testResult ifNil: [testResult := TestResult new]
  318. ! !
  319. !HLSUnitModel methodsFor: 'actions'!
  320. invertSelectedClasses
  321. self testClasses do: [:each |
  322. (self unfilteredSelectedClasses includes: each)
  323. ifTrue: [ self unselectClass: each ]
  324. ifFalse: [ self selectClass: each ]].
  325. !
  326. invertSelectedPackages
  327. self testPackages do: [:each |
  328. (self selectedPackages includes: each)
  329. ifTrue: [ self unselectPackage: each ]
  330. ifFalse: [ self selectPackage: each ]].
  331. !
  332. runTests
  333. | worker |
  334. worker := TestSuiteRunner on: self testCases.
  335. testResult := worker result.
  336. self announcer announce: (HLRunTests on: worker).
  337. self subscribeToTestSuite: worker.
  338. worker run
  339. !
  340. selectAllClasses
  341. self testClasses do: [:each | self selectClass: each].
  342. !
  343. selectAllPackages
  344. self testPackages do: [:each | self selectPackage: each].
  345. !
  346. selectClass: aClass
  347. self unfilteredSelectedClasses add: aClass.
  348. self announcer announce: (HLClassSelected on: aClass).
  349. !
  350. selectPackage: aPackage
  351. self selectedPackages add: aPackage.
  352. self announcer announce: (HLPackageSelected on: aPackage).
  353. !
  354. subscribeToTestSuite: aTestSuiteRunner
  355. currentSuite ifNotNil: [ currentSuite announcer unsubscribe: self].
  356. currentSuite := aTestSuiteRunner.
  357. currentSuite announcer
  358. on: ResultAnnouncement
  359. send: #onResultAnnouncement:
  360. to: self
  361. !
  362. unselectClass: aClass
  363. self unfilteredSelectedClasses remove: aClass ifAbsent: [^self].
  364. self announcer announce: (HLClassUnselected on: aClass).
  365. !
  366. unselectPackage: aPackage
  367. self selectedPackages remove: aPackage ifAbsent: [^self].
  368. self announcer announce: (HLPackageUnselected on: aPackage).
  369. ! !
  370. !HLSUnitModel methodsFor: 'private'!
  371. unfilteredSelectedClasses
  372. ^ (selectedClasses ifNil: [ selectedClasses := Set new ])
  373. ! !
  374. !HLSUnitModel methodsFor: 'reacting'!
  375. onResultAnnouncement: announcement
  376. "Propogate announcement"
  377. self announcer announce: announcement.
  378. ! !
  379. HLToolListWidget subclass: #HLSUnitResultListWidget
  380. instanceVariableNames: ''
  381. package: 'Helios-SUnit'!
  382. !HLSUnitResultListWidget commentStamp!
  383. I group the lists that display test results!
  384. !HLSUnitResultListWidget methodsFor: 'actions'!
  385. performFailure: aTestCase
  386. aTestCase runCase
  387. ! !
  388. !HLSUnitResultListWidget methodsFor: 'initialization'!
  389. observeModel
  390. self model announcer
  391. on: ResultAnnouncement
  392. send: #onResultAnnouncement:
  393. to: self
  394. ! !
  395. !HLSUnitResultListWidget methodsFor: 'reacting'!
  396. onResultAnnouncement: announcement
  397. self refresh.
  398. ! !
  399. !HLSUnitResultListWidget methodsFor: 'rendering'!
  400. renderItemLabel: anObject on: html
  401. html with: anObject class name, ' >> ', anObject selector
  402. !
  403. reselectItem: anObject
  404. self performFailure: anObject
  405. ! !
  406. HLSUnitResultListWidget subclass: #HLSUnitErrorsListWidget
  407. instanceVariableNames: ''
  408. package: 'Helios-SUnit'!
  409. !HLSUnitErrorsListWidget commentStamp!
  410. I display a list of tests that have errors!
  411. !HLSUnitErrorsListWidget methodsFor: 'accessing'!
  412. items
  413. ^self model testResult errors
  414. !
  415. label
  416. ^'Errors'
  417. ! !
  418. HLSUnitResultListWidget subclass: #HLSUnitFailuresListWidget
  419. instanceVariableNames: ''
  420. package: 'Helios-SUnit'!
  421. !HLSUnitFailuresListWidget commentStamp!
  422. I display a list of tests that have failures!
  423. !HLSUnitFailuresListWidget methodsFor: 'accessing'!
  424. items
  425. ^self model testResult failures
  426. !
  427. label
  428. ^'Failures'
  429. ! !
  430. HLWidget subclass: #HLSUnitResultStatus
  431. instanceVariableNames: 'model'
  432. package: 'Helios-SUnit'!
  433. !HLSUnitResultStatus commentStamp!
  434. I display the status of the previous test run
  435. 1. How many tests where run.
  436. * How many tests passed.
  437. * How many tests failed.
  438. * How many tests resulted in an error.!
  439. !HLSUnitResultStatus methodsFor: 'accessing'!
  440. model
  441. ^ model ifNil: [model := TestResult new]
  442. !
  443. model: anObject
  444. model := anObject.
  445. self observeModel.
  446. !
  447. result
  448. ^ self model testResult
  449. !
  450. statusCssClass
  451. ^'sunit status ', self result status
  452. !
  453. statusInfo
  454. ^ self printTotal, self printPasses, self printErrors, self printFailures
  455. ! !
  456. !HLSUnitResultStatus methodsFor: 'actions'!
  457. observeModel
  458. self model announcer
  459. on: ResultAnnouncement
  460. send: #onResultAnnouncement:
  461. to: self
  462. ! !
  463. !HLSUnitResultStatus methodsFor: 'printing'!
  464. printErrors
  465. ^ self result errors size asString , ' errors, '
  466. !
  467. printFailures
  468. ^ self result failures size asString, ' failures'
  469. !
  470. printPasses
  471. ^ (self result runs - self result errors size - self result failures size) asString , ' passes, '
  472. !
  473. printTotal
  474. ^ self result total asString, ' runs, '
  475. ! !
  476. !HLSUnitResultStatus methodsFor: 'reacting'!
  477. onResultAnnouncement: announcement
  478. self refresh.
  479. ! !
  480. !HLSUnitResultStatus methodsFor: 'rendering'!
  481. renderContentOn: html
  482. html div
  483. class: self statusCssClass;
  484. with: [ html span with: self statusInfo ]
  485. ! !
  486. HLWidget subclass: #HLSUnitResults
  487. instanceVariableNames: 'model progressBarWidget resultStatusWidget'
  488. package: 'Helios-SUnit'!
  489. !HLSUnitResults commentStamp!
  490. I am the widget that displays the test results for a previous test run in Helios.
  491. I display.
  492. 1. The status of the tests.
  493. * Progress of the currently running test suite.!
  494. !HLSUnitResults methodsFor: 'accessing'!
  495. model
  496. ^model
  497. !
  498. model: anObject
  499. model := anObject.
  500. self observeModel
  501. !
  502. progressBarWidget
  503. ^progressBarWidget ifNil: [progressBarWidget := HLProgressBarWidget new
  504. label: '';
  505. yourself]
  506. !
  507. resultStatusWidget
  508. ^resultStatusWidget ifNil: [resultStatusWidget := HLSUnitResultStatus new
  509. model: self model;
  510. yourself]
  511. ! !
  512. !HLSUnitResults methodsFor: 'initialization'!
  513. observeModel
  514. self model announcer
  515. on: HLRunTests
  516. send: #onRunTests:
  517. to: self;
  518. on: ResultAnnouncement
  519. send: #onResultAnnouncement:
  520. to: self
  521. ! !
  522. !HLSUnitResults methodsFor: 'reacting'!
  523. onResultAnnouncement: announcement
  524. [self progressBarWidget
  525. updateProgress: (self model testResult runs / self model testResult total * 100) rounded] valueWithTimeout: 10
  526. !
  527. onRunTests: announcement
  528. self progressBarWidget updateProgress: 0;
  529. refresh.
  530. ! !
  531. !HLSUnitResults methodsFor: 'rendering'!
  532. renderContentOn: html
  533. html with: self resultStatusWidget;
  534. with: self progressBarWidget
  535. ! !