PageRenderTime 40ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/PHPUnit/PHPUnit/Extensions/TicketListener/GoogleCode.php

https://github.com/jacknicole/sugarcrm_dev
PHP | 275 lines | 150 code | 39 blank | 86 comment | 22 complexity | 59fb3661db7d4b823426b3f1d3df7faf MD5 | raw file
  1. <?php
  2. /**
  3. * PHPUnit
  4. *
  5. * Copyright (c) 2002-2011, Sebastian Bergmann <sebastian@phpunit.de>.
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * * Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * * Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * * Neither the name of Sebastian Bergmann nor the names of his
  21. * contributors may be used to endorse or promote products derived
  22. * from this software without specific prior written permission.
  23. *
  24. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  25. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  26. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  27. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  28. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  29. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  30. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  31. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  32. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  34. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  35. * POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. * @package PHPUnit
  38. * @subpackage Extensions_TicketListener
  39. * @author Jan Sorgalla <jsorgalla@googlemail.com>
  40. * @copyright 2002-2011 Sebastian Bergmann <sebastian@phpunit.de>
  41. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  42. * @link http://www.phpunit.de/
  43. * @since File available since Release 3.5.0
  44. */
  45. /**
  46. * A ticket listener that interacts with the GoogleCode issue API.
  47. *
  48. * @package PHPUnit
  49. * @subpackage Extensions_TicketListener
  50. * @author Jan Sorgalla <jsorgalla@googlemail.com>
  51. * @copyright 2002-2011 Sebastian Bergmann <sebastian@phpunit.de>
  52. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  53. * @version Release: 3.5.14
  54. * @link http://www.phpunit.de/
  55. * @since Class available since Release 3.5.0
  56. */
  57. class PHPUnit_Extensions_TicketListener_GoogleCode extends PHPUnit_Extensions_TicketListener
  58. {
  59. private $email;
  60. private $password;
  61. private $project;
  62. private $statusClosed;
  63. private $statusReopened;
  64. private $printTicketStateChanges;
  65. private $authUrl = 'https://www.google.com/accounts/ClientLogin';
  66. private $apiBaseUrl = 'http://code.google.com/feeds/issues/p/%s/issues';
  67. private $authToken;
  68. /**
  69. * @param string $email The email associated with the Google account.
  70. * @param string $password The password associated with the Google account.
  71. * @param string $project The project name of the system under test (SUT) on Google Code.
  72. * @param string $printTicketChanges Boolean flag to print the ticket state changes in the test result.
  73. * @param string $statusClosed The status name of the closed state.
  74. * @param string $statusReopened The status name of the reopened state.
  75. * @throws RuntimeException
  76. */
  77. public function __construct($email, $password, $project, $printTicketStateChanges = FALSE, $statusClosed = 'Fixed', $statusReopened = 'Started')
  78. {
  79. if (!extension_loaded('curl')) {
  80. throw new RuntimeException('ext/curl is not available');
  81. }
  82. if (!extension_loaded('simplexml')) {
  83. throw new RuntimeException('ext/simplexml is not available');
  84. }
  85. $this->email = $email;
  86. $this->password = $password;
  87. $this->project = $project;
  88. $this->statusClosed = $statusClosed;
  89. $this->statusReopened = $statusReopened;
  90. $this->printTicketStateChanges = $printTicketStateChanges;
  91. $this->apiBaseUrl = sprintf($this->apiBaseUrl, $project);
  92. }
  93. /**
  94. * @param integer $ticketId
  95. * @return array
  96. * @throws RuntimeException
  97. */
  98. public function getTicketInfo($ticketId = NULL)
  99. {
  100. if (!is_numeric($ticketId)) {
  101. return array('status' => 'invalid_ticket_id');
  102. }
  103. $url = $this->apiBaseUrl . '/full/' . $ticketId;
  104. $header = array(
  105. 'Authorization: GoogleLogin auth=' . $this->getAuthToken()
  106. );
  107. list($status, $response) = $this->callGoogleCode($url, $header);
  108. if ($status != 200 || !$response) {
  109. return array('state' => 'unknown_ticket');
  110. }
  111. $ticket = new SimpleXMLElement(str_replace("xmlns=", "ns=", $response));
  112. $result = $ticket->xpath('//issues:state');
  113. $state = (string)$result[0];
  114. if ($state === 'open') {
  115. return array('status' => 'new');
  116. }
  117. if ($state === 'closed') {
  118. return array('status' => 'closed');
  119. }
  120. return array('status' => $state);
  121. }
  122. /**
  123. * @param string $ticketId The ticket number of the ticket under test (TUT).
  124. * @param string $statusToBe The status of the TUT after running the associated test.
  125. * @param string $message The additional message for the TUT.
  126. * @param string $resolution The resolution for the TUT.
  127. * @throws RuntimeException
  128. */
  129. protected function updateTicket($ticketId, $statusToBe, $message, $resolution)
  130. {
  131. $url = $this->apiBaseUrl . '/' . $ticketId . '/comments/full';
  132. $header = array(
  133. 'Authorization: GoogleLogin auth=' . $this->getAuthToken(),
  134. 'Content-Type: application/atom+xml'
  135. );
  136. if ($statusToBe == 'closed') {
  137. $ticketStatus = $this->statusClosed;
  138. } else {
  139. $ticketStatus = $this->statusReopened;
  140. }
  141. list($author,) = explode('@', $this->email);
  142. $post = '<?xml version="1.0" encoding="UTF-8"?>' .
  143. '<entry xmlns="http://www.w3.org/2005/Atom" ' .
  144. ' xmlns:issues="http://schemas.google.com/projecthosting/issues/2009">' .
  145. ' <content type="html">' . htmlspecialchars($message, ENT_COMPAT, 'UTF-8') . '</content>' .
  146. ' <author>' .
  147. ' <name>' . htmlspecialchars($author, ENT_COMPAT, 'UTF-8') . '</name>' .
  148. ' </author>' .
  149. ' <issues:updates>' .
  150. ' <issues:status>' . htmlspecialchars($ticketStatus, ENT_COMPAT, 'UTF-8') . '</issues:status>' .
  151. ' </issues:updates>' .
  152. '</entry>';
  153. list($status, $response) = $this->callGoogleCode($url, $header, $post);
  154. if ($status != 201) {
  155. throw new RuntimeException('Updating GoogleCode issue failed with status code ' . $status);
  156. }
  157. if ($this->printTicketStateChanges) {
  158. printf(
  159. "\nUpdating GoogleCode issue #%d, status: %s\n",
  160. $ticketId,
  161. $statusToBe
  162. );
  163. }
  164. }
  165. /**
  166. * @return string The auth token
  167. * @throws RuntimeException
  168. */
  169. private function getAuthToken()
  170. {
  171. if (NULL !== $this->authToken) {
  172. return $this->authToken;
  173. }
  174. $header = array(
  175. 'Content-Type: application/x-www-form-urlencoded',
  176. );
  177. $post = array(
  178. 'accountType' => 'GOOGLE',
  179. 'Email' => $this->email,
  180. 'Passwd' => $this->password,
  181. 'service' => 'code',
  182. 'source' => 'PHPUnit-TicketListener_GoogleCode-' . PHPUnit_Runner_Version::id(),
  183. );
  184. list($status, $response) = $this->callGoogleCode(
  185. $this->authUrl,
  186. $header,
  187. http_build_query($post, NULL, '&')
  188. );
  189. if ($status != 200) {
  190. throw new RuntimeException('Google account authentication failed');
  191. }
  192. foreach (explode("\n", $response) as $line) {
  193. if (strpos(trim($line), 'Auth') === 0) {
  194. list($name, $token) = explode('=', $line);
  195. $this->authToken = trim($token);
  196. break;
  197. }
  198. }
  199. if (NULL === $this->authToken) {
  200. throw new RuntimeException('Could not detect auth token in response');
  201. }
  202. return $this->authToken;
  203. }
  204. /**
  205. * @param string $url URL to call
  206. * @param array $header Header
  207. * @param string $post Post data
  208. * @return array
  209. */
  210. private function callGoogleCode($url, array $header = NULL, $post = NULL)
  211. {
  212. $curlHandle = curl_init();
  213. curl_setopt($curlHandle, CURLOPT_URL, $url);
  214. curl_setopt($curlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
  215. curl_setopt($curlHandle, CURLOPT_FAILONERROR, TRUE);
  216. curl_setopt($curlHandle, CURLOPT_FRESH_CONNECT, TRUE);
  217. curl_setopt($curlHandle, CURLOPT_RETURNTRANSFER, TRUE);
  218. curl_setopt($curlHandle, CURLOPT_HTTPPROXYTUNNEL, TRUE);
  219. curl_setopt($curlHandle, CURLOPT_USERAGENT, __CLASS__);
  220. curl_setopt($curlHandle, CURLOPT_SSL_VERIFYPEER, FALSE);
  221. if (NULL !== $header) {
  222. curl_setopt($curlHandle, CURLOPT_HTTPHEADER, $header);
  223. }
  224. if (NULL !== $post) {
  225. curl_setopt($curlHandle, CURLOPT_POST, TRUE);
  226. curl_setopt($curlHandle, CURLOPT_POSTFIELDS, $post);
  227. }
  228. $response = curl_exec($curlHandle);
  229. $status = curl_getinfo($curlHandle, CURLINFO_HTTP_CODE);
  230. if (!$response) {
  231. throw new RuntimeException(curl_error($curlHandle));
  232. }
  233. curl_close($curlHandle);
  234. return array($status, $response);
  235. }
  236. }