PHPUnitTask.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. <?php
  2. /**
  3. * $Id: PHPUnitTask.php 905 2010-10-05 16:28:03Z mrook $
  4. *
  5. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16. *
  17. * This software consists of voluntary contributions made by many individuals
  18. * and is licensed under the LGPL. For more information please see
  19. * <http://phing.info>.
  20. */
  21. require_once 'phing/Task.php';
  22. require_once 'phing/system/io/PhingFile.php';
  23. require_once 'phing/system/io/Writer.php';
  24. require_once 'phing/util/LogWriter.php';
  25. /**
  26. * Runs PHPUnit tests.
  27. *
  28. * @author Michiel Rook <michiel.rook@gmail.com>
  29. * @version $Id: PHPUnitTask.php 905 2010-10-05 16:28:03Z mrook $
  30. * @package phing.tasks.ext.phpunit
  31. * @see BatchTest
  32. * @since 2.1.0
  33. */
  34. class PHPUnitTask extends Task
  35. {
  36. private $batchtests = array();
  37. private $formatters = array();
  38. private $bootstrap = "";
  39. private $haltonerror = false;
  40. private $haltonfailure = false;
  41. private $haltonincomplete = false;
  42. private $haltonskipped = false;
  43. private $errorproperty;
  44. private $failureproperty;
  45. private $incompleteproperty;
  46. private $skippedproperty;
  47. private $printsummary = false;
  48. private $testfailed = false;
  49. private $testfailuremessage = "";
  50. private $codecoverage = false;
  51. private $groups = array();
  52. private $excludeGroups = array();
  53. private $usecustomerrorhandler = true;
  54. /**
  55. * Initialize Task.
  56. * This method includes any necessary PHPUnit2 libraries and triggers
  57. * appropriate error if they cannot be found. This is not done in header
  58. * because we may want this class to be loaded w/o triggering an error.
  59. */
  60. public function init() {
  61. if (version_compare(PHP_VERSION, '5.0.3') < 0)
  62. {
  63. throw new BuildException("PHPUnitTask requires PHP version >= 5.0.3", $this->getLocation());
  64. }
  65. /**
  66. * Determine PHPUnit version number
  67. */
  68. @include_once 'PHPUnit/Runner/Version.php';
  69. $version = PHPUnit_Runner_Version::id();
  70. if (version_compare($version, '3.2.0') < 0)
  71. {
  72. throw new BuildException("PHPUnitTask requires PHPUnit version >= 3.2.0", $this->getLocation());
  73. }
  74. /**
  75. * Other dependencies that should only be loaded when class is actually used.
  76. */
  77. require_once 'phing/tasks/ext/phpunit/PHPUnitTestRunner.php';
  78. require_once 'phing/tasks/ext/phpunit/BatchTest.php';
  79. require_once 'phing/tasks/ext/phpunit/FormatterElement.php';
  80. /**
  81. * Add some defaults to the PHPUnit filter
  82. */
  83. $pwd = dirname(__FILE__);
  84. require_once 'PHPUnit/Framework.php';
  85. require_once 'PHPUnit/Util/Filter.php';
  86. // point PHPUnit_MAIN_METHOD define to non-existing method
  87. if (!defined('PHPUnit_MAIN_METHOD'))
  88. {
  89. define('PHPUnit_MAIN_METHOD', 'PHPUnitTask::undefined');
  90. }
  91. $path = realpath($pwd . '/../../../');
  92. if (version_compare($version, '3.5.0') >= 0) {
  93. PHP_CodeCoverage_Filter::getInstance()->addDirectoryToBlacklist($path);
  94. } else {
  95. PHPUnit_Util_Filter::addDirectoryToFilter($path);
  96. }
  97. }
  98. /**
  99. * Sets the name of a bootstrap file that is run before
  100. * executing the tests
  101. *
  102. * @param string $bootstrap the name of the bootstrap file
  103. */
  104. public function setBootstrap($bootstrap)
  105. {
  106. $this->bootstrap = $bootstrap;
  107. }
  108. public function setErrorproperty($value)
  109. {
  110. $this->errorproperty = $value;
  111. }
  112. public function setFailureproperty($value)
  113. {
  114. $this->failureproperty = $value;
  115. }
  116. public function setIncompleteproperty($value)
  117. {
  118. $this->incompleteproperty = $value;
  119. }
  120. public function setSkippedproperty($value)
  121. {
  122. $this->skippedproperty = $value;
  123. }
  124. public function setHaltonerror($value)
  125. {
  126. $this->haltonerror = $value;
  127. }
  128. public function setHaltonfailure($value)
  129. {
  130. $this->haltonfailure = $value;
  131. }
  132. public function getHaltonfailure()
  133. {
  134. return $this->haltonfailure;
  135. }
  136. public function setHaltonincomplete($value)
  137. {
  138. $this->haltonincomplete = $value;
  139. }
  140. public function getHaltonincomplete()
  141. {
  142. return $this->haltonincomplete;
  143. }
  144. public function setHaltonskipped($value)
  145. {
  146. $this->haltonskipped = $value;
  147. }
  148. public function getHaltonskipped()
  149. {
  150. return $this->haltonskipped;
  151. }
  152. public function setPrintsummary($printsummary)
  153. {
  154. $this->printsummary = $printsummary;
  155. }
  156. public function setCodecoverage($codecoverage)
  157. {
  158. $this->codecoverage = $codecoverage;
  159. }
  160. public function setUseCustomErrorHandler($usecustomerrorhandler)
  161. {
  162. $this->usecustomerrorhandler = $usecustomerrorhandler;
  163. }
  164. public function setGroups($groups)
  165. {
  166. $token = ' ,;';
  167. $this->groups = array();
  168. $tok = strtok($groups, $token);
  169. while ($tok !== false) {
  170. $this->groups[] = $tok;
  171. $tok = strtok($token);
  172. }
  173. }
  174. public function setExcludeGroups($excludeGroups)
  175. {
  176. $token = ' ,;';
  177. $this->excludeGroups = array();
  178. $tok = strtok($excludeGroups, $token);
  179. while ($tok !== false) {
  180. $this->excludeGroups[] = $tok;
  181. $tok = strtok($token);
  182. }
  183. }
  184. /**
  185. * Add a new formatter to all tests of this task.
  186. *
  187. * @param FormatterElement formatter element
  188. */
  189. public function addFormatter(FormatterElement $fe)
  190. {
  191. $fe->setParent($this);
  192. $this->formatters[] = $fe;
  193. }
  194. /**
  195. * The main entry point
  196. *
  197. * @throws BuildException
  198. */
  199. public function main()
  200. {
  201. if ($this->codecoverage && !extension_loaded('xdebug'))
  202. {
  203. throw new Exception("PHPUnitTask depends on Xdebug being installed to gather code coverage information.");
  204. }
  205. if ($this->printsummary)
  206. {
  207. $fe = new FormatterElement();
  208. $fe->setParent($this);
  209. $fe->setType("summary");
  210. $fe->setUseFile(false);
  211. $this->formatters[] = $fe;
  212. }
  213. if ($this->bootstrap)
  214. {
  215. require_once $this->bootstrap;
  216. }
  217. foreach ($this->formatters as $fe)
  218. {
  219. $formatter = $fe->getFormatter();
  220. if ($fe->getUseFile())
  221. {
  222. $destFile = new PhingFile($fe->getToDir(), $fe->getOutfile());
  223. $writer = new FileWriter($destFile->getAbsolutePath());
  224. $formatter->setOutput($writer);
  225. }
  226. else
  227. {
  228. $formatter->setOutput($this->getDefaultOutput());
  229. }
  230. $formatter->startTestRun();
  231. }
  232. foreach ($this->batchtests as $batchtest)
  233. {
  234. $this->execute($batchtest->getTestSuite());
  235. }
  236. foreach ($this->formatters as $fe)
  237. {
  238. $formatter = $fe->getFormatter();
  239. $formatter->endTestRun();
  240. }
  241. if ($this->testfailed)
  242. {
  243. throw new BuildException($this->testfailuremessage);
  244. }
  245. }
  246. /**
  247. * @throws BuildException
  248. */
  249. protected function execute($suite)
  250. {
  251. $runner = new PHPUnitTestRunner($this->project, $this->groups, $this->excludeGroups);
  252. $runner->setCodecoverage($this->codecoverage);
  253. $runner->setUseCustomErrorHandler($this->usecustomerrorhandler);
  254. foreach ($this->formatters as $fe)
  255. {
  256. $formatter = $fe->getFormatter();
  257. $runner->addFormatter($formatter);
  258. }
  259. $runner->run($suite);
  260. $retcode = $runner->getRetCode();
  261. if ($retcode == PHPUnitTestRunner::ERRORS) {
  262. if ($this->errorproperty) {
  263. $this->project->setNewProperty($this->errorproperty, true);
  264. }
  265. if ($this->haltonerror) {
  266. $this->testfailed = true;
  267. $this->testfailuremessage = $runner->getLastFailureMessage();
  268. }
  269. } elseif ($retcode == PHPUnitTestRunner::FAILURES) {
  270. if ($this->failureproperty) {
  271. $this->project->setNewProperty($this->failureproperty, true);
  272. }
  273. if ($this->haltonfailure) {
  274. $this->testfailed = true;
  275. $this->testfailuremessage = $runner->getLastFailureMessage();
  276. }
  277. } elseif ($retcode == PHPUnitTestRunner::INCOMPLETES) {
  278. if ($this->incompleteproperty) {
  279. $this->project->setNewProperty($this->incompleteproperty, true);
  280. }
  281. if ($this->haltonincomplete) {
  282. $this->testfailed = true;
  283. $this->testfailuremessage = $runner->getLastFailureMessage();
  284. }
  285. } elseif ($retcode == PHPUnitTestRunner::SKIPPED) {
  286. if ($this->skippedproperty) {
  287. $this->project->setNewProperty($this->skippedproperty, true);
  288. }
  289. if ($this->haltonskipped) {
  290. $this->testfailed = true;
  291. $this->testfailuremessage = $runner->getLastFailureMessage();
  292. }
  293. }
  294. }
  295. protected function getDefaultOutput()
  296. {
  297. return new LogWriter($this);
  298. }
  299. /**
  300. * Adds a set of tests based on pattern matching.
  301. *
  302. * @return BatchTest a new instance of a batch test.
  303. */
  304. public function createBatchTest()
  305. {
  306. $batchtest = new BatchTest($this->getProject());
  307. $this->batchtests[] = $batchtest;
  308. return $batchtest;
  309. }
  310. }