3 * Copyright (C) 2007-2008 Alain Peyrat <aljeux at free dot fr>
4 * Copyright (C) 2009 Alain Peyrat, Alcatel-Lucent
5 * Copyright 2013, Franck Villaume - TrivialDev
7 * This file is part of FusionForge.
9 * FusionForge is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published
11 * by the Free Software Foundation; either version 2 of the License,
12 * or (at your option) any later version.
14 * FusionForge is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * Standard Alcatel-Lucent disclaimer for contributing to open source
27 * "The test suite ("Contribution") has not been tested and/or
28 * validated for release as or in products, combinations with products or
29 * other commercial use. Any use of the Contribution is entirely made at
30 * the user's own responsibility and the user can not rely on any features,
31 * functionalities or performances Alcatel-Lucent has attributed to the
34 * THE CONTRIBUTION BY ALCATEL-LUCENT IS PROVIDED AS IS, WITHOUT WARRANTY
35 * OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
36 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, COMPLIANCE,
37 * NON-INTERFERENCE AND/OR INTERWORKING WITH THE SOFTWARE TO WHICH THE
38 * CONTRIBUTION HAS BEEN MADE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
39 * ALCATEL-LUCENT BE LIABLE FOR ANY DAMAGES OR OTHER LIABLITY, WHETHER IN
40 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
41 * CONTRIBUTION OR THE USE OR OTHER DEALINGS IN THE CONTRIBUTION, WHETHER
42 * TOGETHER WITH THE SOFTWARE TO WHICH THE CONTRIBUTION RELATES OR ON A STAND
46 $config = getenv('CONFIG_PHP') ? getenv('CONFIG_PHP'): dirname(dirname(__FILE__)).'/config.php';
49 require_once 'PHPUnit/Extensions/SeleniumTestCase.php';
51 class FForge_SeleniumTestCase extends PHPUnit_Extensions_SeleniumTestCase
53 protected $logged_in = false ;
55 protected function setUp()
57 if (getenv('SELENIUM_RC_DIR') && getenv('SELENIUM_RC_URL')) {
58 $this->captureScreenshotOnFailure = true;
59 $this->screenshotPath = getenv('SELENIUM_RC_DIR');
60 $this->screenshotUrl = getenv('SELENIUM_RC_URL');
62 if (defined('DB_INIT_CMD')) {
63 // Reload a fresh database before running this test suite.
65 passthru(DB_INIT_CMD, $ret);
68 die('DB_INIT_CMD ('.DB_INIT_CMD.') failed');
71 $this->setBrowser('*firefox');
72 $this->setBrowserUrl(URL);
73 $this->setHost(SELENIUM_RC_HOST);
75 // Use a sensible default background (instead of Selenium's criminal default to black)
76 // (future-proof - https://github.com/giorgiosironi/phpunit-selenium/commit/07e50f74f3782ce8781527653e6c79aeefd94ada)
77 $this->screenshotBgColor = '#CCFFDD';
80 protected function changeConfig($text) {
81 $forge_get_config = RUN_JOB_PATH."/forge_get_config";
82 $config_path = rtrim(`$forge_get_config config_path`);
83 $classname = get_class($this);
84 file_put_contents("$config_path/config.ini.d/zzz-buildbot-$classname.ini",
89 * Method that is called after Selenium actions.
91 * @param string $action
93 protected function defaultAssertions($action)
95 if ($action == 'waitForPageToLoad') {
96 $this->assertElementPresent("//h1");
97 // $this->assertFalse($this->isElementPresent("//div[@id='ffErrors']"));
98 // $this->assertFalse($this->isTextPresent("PhpWiki Warning:"));
102 protected function clickAndWait($link)
105 $this->waitForPageToLoad();
108 protected function waitForTextPresent($text)
110 for ($second = 0; ; $second++) {
111 if ($second >= 30) $this->fail("timeout");
113 if ($this->isTextPresent($text)) break;
114 } catch (Exception $e) {}
119 protected function runCommand($cmd)
121 system(RUN_COMMAND_PREFIX.$cmd, $ret);
122 $this->assertEquals($ret, 0);
125 protected function db($sql)
127 system("echo \"$sql\" | psql -q -Upostgres ".DB_NAME);
130 protected function cron($cmd)
132 $this->runCommand(RUN_JOB_PATH."/forge_run_job $cmd");
135 protected function cron_for_plugin($cmd, $plugin)
137 $this->runCommand(RUN_JOB_PATH."/forge_run_plugin_job $plugin $cmd");
141 * Execute pending system tasks
143 protected function waitSystasks()
145 $this->runCommand(RUN_JOB_PATH.'/systasks_wait_until_empty.php');
148 protected function reload_apache()
150 $this->runCommand("service apache2 reload > /dev/null 2>&1 || service httpd reload > /dev/null 2>&1");
151 sleep (3); // Give it some time to become available again
154 protected function init() {
155 $this->createAndGoto('ProjectA');
158 protected function populateStandardTemplate($what='all')
160 if ($what == 'all') {
161 $what = array('trackers','tasks','forums');
162 } elseif ($what == 'empty') {
164 } elseif (!is_array($what)) {
165 $what = array($what) ;
167 $this->switchUser (FORGE_ADMIN_USERNAME) ;
169 $this->createProject ('Tmpl');
171 $this->click("link=Site Admin");
172 $this->waitForPageToLoad("30000");
173 $this->click("link=Display Full Project List/Edit Projects");
174 $this->waitForPageToLoad("30000");
175 $this->click("link=Tmpl");
176 $this->waitForPageToLoad("30000");
177 $this->select ("//select[@name='form_template']", "label=Yes") ;
178 $this->click("submit");
179 $this->waitForPageToLoad("30000");
181 $this->open( ROOT . '/projects/tmpl') ;
182 $this->waitForPageToLoad("30000");
184 $this->click("link=Admin");
185 $this->waitForPageToLoad("30000");
186 $this->click("link=Tools");
187 $this->waitForPageToLoad("30000");
188 $this->check("//input[@name='use_forum']") ;
189 $this->check("//input[@name='use_tracker']") ;
190 $this->check("//input[@name='use_mail']") ;
191 $this->check("//input[@name='use_pm']") ;
192 $this->check("//input[@name='use_docman']") ;
193 $this->check("//input[@name='use_news']") ;
194 $this->check("//input[@name='use_frs']") ;
195 $this->click("submit");
196 $this->waitForPageToLoad("30000");
198 if (in_array ('trackers', $what)) {
199 $this->click("link=Trackers Administration");
200 $this->waitForPageToLoad("30000");
201 $this->type("name", "Bugs");
202 $this->type("//input[@name='description']", "Tracker for bug reports");
203 $this->click("post_changes");
204 $this->waitForPageToLoad("30000");
205 $this->assertTrue($this->isTextPresent("Tracker created successfully"));
206 $this->click("link=Bugs");
207 $this->waitForPageToLoad("30000");
208 $this->click("link=Manage Custom Fields");
209 $this->waitForPageToLoad("30000");
210 $this->type("name", "URL");
211 $this->type("alias", "url");
212 $this->click("//input[@name='field_type' and @value=4]");
213 $this->click("post_changes");
214 $this->waitForPageToLoad("30000");
216 $this->click("link=Admin");
217 $this->waitForPageToLoad("30000");
218 $this->click("link=Tools");
219 $this->waitForPageToLoad("30000");
220 $this->click("link=Trackers Administration");
221 $this->waitForPageToLoad("30000");
222 $this->type("name", "Support Requests");
223 $this->type("//input[@name='description']", "Tracker for support requests");
224 $this->click("post_changes");
225 $this->waitForPageToLoad("30000");
226 $this->assertTrue($this->isTextPresent("Tracker created successfully"));
228 $this->type("name", "Patches");
229 $this->type("//input[@name='description']", "Proposed changes to code");
230 $this->click("post_changes");
231 $this->waitForPageToLoad("30000");
232 $this->assertTrue($this->isTextPresent("Tracker created successfully"));
234 $this->type("name", "Feature Requests");
235 $this->type("//input[@name='description']", "New features that people want");
236 $this->click("post_changes");
237 $this->waitForPageToLoad("30000");
238 $this->assertTrue($this->isTextPresent("Tracker created successfully"));
241 if (in_array ('tasks', $what)) {
242 $this->click("link=Admin");
243 $this->waitForPageToLoad("30000");
244 $this->click("link=Tools");
245 $this->waitForPageToLoad("30000");
246 $this->click("link=Tasks Administration");
247 $this->waitForPageToLoad("30000");
248 $this->click("link=Add a Subproject");
249 $this->waitForPageToLoad("30000");
250 $this->type("project_name", "To Do");
251 $this->type("//input[@name='description']", "Things we have to do");
252 $this->click("submit");
253 $this->waitForPageToLoad("30000");
254 $this->assertTrue($this->isTextPresent("Subproject Inserted"));
256 $this->type("project_name", "Next Release");
257 $this->type("//input[@name='description']", "Items for our next release");
258 $this->click("submit");
259 $this->waitForPageToLoad("30000");
260 $this->assertTrue($this->isTextPresent("Subproject Inserted"));
263 if (in_array ('forums', $what)) {
264 $this->click("link=Admin");
265 $this->waitForPageToLoad("30000");
266 $this->click("link=Tools");
267 $this->waitForPageToLoad("30000");
268 $this->click("link=Forums Admin");
269 $this->waitForPageToLoad("30000");
271 $this->click("link=Add Forum");
272 $this->waitForPageToLoad("30000");
273 $this->type("forum_name", "Open-Discussion");
274 $this->type("//input[@name='description']", "General Discussion");
275 $this->click("submit");
276 $this->waitForPageToLoad("30000");
277 $this->assertTrue($this->isTextPresent("Forum added successfully"));
279 $this->type("forum_name", "Help");
280 $this->type("//input[@name='description']", "Get Public Help");
281 $this->click("submit");
282 $this->waitForPageToLoad("30000");
283 $this->assertTrue($this->isTextPresent("Forum added successfully"));
285 $this->type("forum_name", "Developers-Discussion");
286 $this->type("//input[@name='description']", "Project Developer Discussion");
287 $this->click("submit");
288 $this->waitForPageToLoad("30000");
289 $this->assertTrue($this->isTextPresent("Forum added successfully"));
291 $this->click("link=Forums");
292 $this->waitForPageToLoad("30000");
293 $this->assertTrue($this->isTextPresent("open-discussion"));
294 $this->assertTrue($this->isTextPresent("Get Public Help"));
295 $this->assertTrue($this->isTextPresent("Project Developer Discussion"));
299 protected function initSvn($project='ProjectA', $user=FORGE_ADMIN_USERNAME)
301 // Remove svnroot directory before creating the project.
302 $forge_get_config = RUN_JOB_PATH."/forge_get_config";
303 $repo = `$forge_get_config chroot`.'/scmrepos/svn/'.strtolower($project);
305 system("rm -fr $repo");
308 $this->init($project, $user);
310 // Run manually the cron for creating the svn structure.
311 $this->cron("scm/create_scm_repos.php");
314 protected function login($username)
317 if ($this->isTextPresent('Log Out')) {
320 $this->clickAndWait("link=Log In");
321 $this->triggeredLogin($username);
324 protected function triggeredLogin($username)
326 if ($username == FORGE_ADMIN_USERNAME) {
327 $password = FORGE_ADMIN_PASSWORD;
329 $password = FORGE_OTHER_PASSWORD;
332 $this->type("form_loginname", $username);
333 $this->type("form_pw", $password);
334 $this->clickAndWait("login");
336 $this->logged_in = $username ;
339 protected function logout()
341 // $this->click("link=Log Out");
342 $this->open( ROOT ."/account/logout.php" );
343 $this->waitForPageToLoad("30000");
345 $this->logged_in = false ;
348 protected function switchUser($username)
350 if ($this->logged_in != $username) {
352 $this->login($username);
356 protected function isLoginRequired()
358 return $this->isTextPresent("You've been redirected to this login page") ;
361 protected function isPermissionDenied()
363 return $this->isTextPresent("Permission denied") ;
366 protected function registerProject ($name, $user, $scm='scmsvn') {
367 $unix_name = strtolower($name);
369 $saved_user = $this->logged_in ;
370 $this->switchUser ($user) ;
372 $this->clickAndWait("link=My Page");
373 $this->clickAndWait("link=Register Project");
374 $this->type("full_name", $name);
375 $this->type("purpose", "This is a simple description for $name");
376 $this->type("//textarea[@name='description']", "This is the public description for $name.");
377 $this->type("unix_name", $unix_name);
378 $this->click("//input[@name='scm' and @value='$scm']");
380 if ($this->isElementPresent("//select[@name='built_from_template']/option[.='Tmpl']")) {
381 $this->select("//select[@name='built_from_template']", "label=Tmpl");
384 $this->clickAndWait("submit");
385 $this->assertTextPresent("Your project has been automatically approved");
387 $this->switchUser ($saved_user) ;
390 protected function approveProject ($name, $user) {
391 $unix_name = strtolower($name);
393 $saved_user = $this->logged_in ;
394 $this->switchUser ($user) ;
396 $this->open( ROOT . '/admin/approve-pending.php') ;
397 $this->waitForPageToLoad("30000");
398 $this->click("document.forms['approve.$unix_name'].submit");
399 $this->waitForPageToLoad("60000");
401 $this->assertTrue($this->isTextPresent("Approving Project: $unix_name"));
403 $this->switchUser ($saved_user) ;
406 protected function createProject ($name, $scm='scmsvn') {
407 $unix_name = strtolower($name);
409 $this->switchUser (FORGE_ADMIN_USERNAME) ;
411 // Create a simple project.
412 if ((!defined('PROJECTA')) || ($unix_name != "projecta")) {
413 $this->registerProject ($name, FORGE_ADMIN_USERNAME, $scm) ;
417 protected function createAndGoto($project) {
418 $this->createProject($project);
419 $this->gotoProject($project);
422 protected function createUser ($login)
425 $this->clickAndWait("link=Site Admin");
426 $this->clickAndWait("link=Register a New User");
427 $this->type("unix_name", $login);
428 $this->type("password1", FORGE_OTHER_PASSWORD);
429 $this->type("password2", FORGE_OTHER_PASSWORD);
430 $this->type("firstname", $login);
431 $this->type("lastname", "Lastname");
432 $this->type("email", $login."@debug.log");
433 $this->clickAndWait("submit");
434 $this->clickAndWait("link=Site Admin");
435 $this->clickAndWait("link=Display Full User List/Edit Users");
436 $this->clickAndWait("//table/tbody/tr/td/a[contains(@href,'useredit.php') and contains(.,'($login)')]/../..//a[contains(@href, 'userlist.php?action=activate&user_id=')]");
439 protected function activatePlugin($pluginName) {
440 $this->switchUser(FORGE_ADMIN_USERNAME);
441 $this->open( ROOT . '/admin/pluginman.php?update='.$pluginName.'&action=deactivate');
442 $this->waitForPageToLoad("30000");
443 $this->open( ROOT . '/admin/pluginman.php?update='.$pluginName.'&action=activate');
444 $this->waitForPageToLoad("30000");
448 protected function gotoProject($project) {
449 $unix_name = strtolower($project);
451 $this->open( ROOT . '/projects/' . $unix_name) ;
452 $this->waitForPageToLoad("30000");
453 $this->assertTrue($this->isTextPresent("This is the public description for $project."));
456 protected function uploadSshKey () {
457 // Prepare client config
458 $sshdir = getenv('HOME') . '/.ssh';
459 if (!file_exists($sshdir)) {
461 chmod($sshdir, 0700);
463 $config = $sshdir . '/config';
464 if (!file_exists($config) or
465 // Avoid OpenSSH host fingerprint prompt
466 count(preg_grep('/StrictHostKeyChecking/', file($config))) == 0) {
467 $f = fopen($config, 'a');
468 fwrite($f, 'StrictHostKeyChecking no');
471 chmod($sshdir . '/config', 0600);
473 // Generate user keys
474 $privkey = $sshdir . '/id_rsa';
475 $pubkey = $sshdir . '/id_rsa.pub';
476 if (!file_exists($pubkey)) {
477 system("ssh-keygen -N '' -f $privkey");
480 // Upload keys to the web interface
481 $keys = file($pubkey);
483 $this->assertEquals(count($keys), 1);
484 $this->clickAndWait("link=My Account");
485 $this->clickAndWait("link=Edit Keys");
486 $this->type("authorized_key", $k);
487 $this->clickAndWait("submit");
490 protected function skip_test($msg) {
491 $this->captureScreenshotOnFailure = false;
492 $this->markTestSkipped($msg);
495 protected function skip_on_rpm_installs($msg='Skipping on installations from RPM') {
496 if (INSTALL_METHOD == 'rpm') {
497 $this->skip_test($msg);
501 protected function skip_on_deb_installs($msg='Skipping on installations from *.deb') {
502 if (INSTALL_METHOD == 'deb') {
503 $this->skip_test($msg);
507 protected function skip_on_src_installs($msg='Skipping on installations from source') {
508 if (INSTALL_METHOD == 'src') {
509 $this->skip_test($msg);
513 protected function skip_on_centos($msg='Skipping on CentOS platforms') {
514 if (INSTALL_OS == 'centos') {
515 $this->skip_test($msg);
519 protected function skip_on_debian($msg='Skipping on Debian platforms') {
520 if (INSTALL_OS == 'debian') {
521 $this->skip_test($msg);
528 // c-file-style: "bsd"