2 # -*- coding: utf-8 -*-
7 Copyright (c) 2013 Frédéric Massart - FMCorz.net
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 http://github.com/FMCorz/mdk
30 from tempfile
import gettempdir
31 from time
import sleep
32 from ..command
import Command
33 from ..tools
import process
, ProcessInThread
, downloadProcessHook
, question
36 class BehatCommand(Command
):
42 'action': 'store_true',
43 'help': 'run the tests'
49 'action': 'store_true',
50 'help': 'disable Behat, runs the tests first if --run has been set. Ignored from 2.7.'
56 'action': 'store_true',
57 'help': 'force behat re-init and reset the variables in the config file.'
64 'help': 'typically a path to a feature, or an argument understood by behat (see [features]: vendor/bin/behat --help). Automatically convert path to absolute path.'
72 'help': 'only execute the feature elements which match part of the given name or regex'
79 'help': 'only execute the features or scenarios with tags matching tag filter expression'
83 ['-j', '--no-javascript'],
85 'action': 'store_true',
86 'dest': 'nojavascript',
87 'help': 'do not start Selenium and ignore Javascript (short for --tags=~@javascript). Cannot be combined with --tags or --testname.'
93 'action': 'store_false',
95 'help': 'use the standard command without fancy screenshots or output to a directory'
99 ['-s', '--switch-completely'],
101 'action': 'store_true',
102 'dest': 'switchcompletely',
103 'help': 'force the switch completely setting. This will be automatically enabled for PHP < 5.4. Ignored from 2.7.'
111 'help': 'path to the selenium standalone server to use',
116 ['--selenium-download'],
118 'action': 'store_true',
119 'dest': 'seleniumforcedl',
120 'help': 'force the download of the latest Selenium to the cache'
124 ['--selenium-verbose'],
126 'action': 'store_true',
127 'dest': 'seleniumverbose',
128 'help': 'outputs the output from selenium in the same window'
135 'help': 'name of the instance',
141 _description
= 'Initialise Behat'
146 M
= self
.Wp
.resolve(args
.name
)
148 raise Exception('This is not a Moodle instance')
150 # Check required version
151 if M
.branch_compare(25, '<'):
152 raise Exception('Behat is only available from Moodle 2.5')
155 if not M
.get('installed'):
156 raise Exception('This instance needs to be installed first')
159 if args
.disable
and not args
.run
:
164 nojavascript
= args
.nojavascript
165 if not nojavascript
and not self
.C
.get('java') or not os
.path
.isfile(os
.path
.abspath(self
.C
.get('java'))):
167 logging
.info('Disabling Javascript because Java is required to run Selenium and could not be found.')
169 # If not composer.phar, install Composer
170 if not os
.path
.isfile(os
.path
.join(M
.get('path'), 'composer.phar')):
171 logging
.info('Installing Composer')
172 cliFile
= 'behat_install_composer.php'
173 cliPath
= os
.path
.join(M
.get('path'), 'behat_install_composer.php')
174 (to
, headers
) = urllib
.urlretrieve('http://getcomposer.org/installer', cliPath
)
175 if headers
.dict.get('content-encoding') == 'gzip':
176 f
= gzip
.open(cliPath
, 'r')
179 f
= open(cliPath
, 'w')
182 M
.cli('/' + cliFile
, stdout
=None, stderr
=None)
184 M
.cli('composer.phar', args
='install --dev', stdout
=None, stderr
=None)
187 seleniumPath
= os
.path
.expanduser(os
.path
.join(self
.C
.get('dirs.mdk'), 'selenium.jar'))
189 seleniumPath
= args
.selenium
190 elif args
.seleniumforcedl
or (not nojavascript
and not os
.path
.isfile(seleniumPath
)):
191 logging
.info('Attempting to find a download for Selenium')
192 url
= urllib
.urlopen('http://docs.seleniumhq.org/download/')
194 selenium
= re
.search(r
'http:[a-z0-9/._-]+selenium-server-standalone-[0-9.]+\.jar', content
, re
.I
)
196 logging
.info('Downloading Selenium from %s' %
(selenium
.group(0)))
197 if (logging
.getLogger().level
<= logging
.INFO
):
198 urllib
.urlretrieve(selenium
.group(0), seleniumPath
, downloadProcessHook
)
199 # Force a new line after the hook display
202 urllib
.urlretrieve(selenium
.group(0), seleniumPath
)
204 logging
.warning('Could not locate Selenium server to download')
206 if not nojavascript
and not os
.path
.isfile(seleniumPath
):
207 raise Exception('Selenium file %s does not exist')
212 # If Oracle, ask the user for a Behat prefix, if not set.
213 prefix
= M
.get('behat_prefix')
214 if M
.get('dbtype') == 'oci' and (args
.force
or not prefix
or len(prefix
) > 2):
215 while not prefix
or len(prefix
) > 2:
216 prefix
= question('What prefix would you like to use? (Oracle, max 2 chars)')
220 outputDir
= self
.Wp
.getExtraDir(M
.get('identifier'), 'behat')
221 outpurUrl
= self
.Wp
.getUrl(M
.get('identifier'), extra
='behat')
223 logging
.info('Initialising Behat, please be patient!')
224 M
.initBehat(switchcompletely
=args
.switchcompletely
, force
=args
.force
, prefix
=prefix
, faildumppath
=outputDir
)
225 logging
.info('Behat ready!')
227 # Preparing Behat command
228 cmd
= ['vendor/bin/behat']
230 cmd
.append('--tags="%s"' %
(args
.tags
))
233 cmd
.append('--name="%s"' %
(args
.testname
))
235 if not (args
.tags
or args
.testname
) and nojavascript
:
236 cmd
.append('--tags ~@javascript')
239 cmd
.append('--format="progress,progress,pretty,html,failed"')
240 cmd
.append('--out=",{0}/progress.txt,{0}/pretty.txt,{0}/status.html,{0}/failed.txt"'.format(outputDir
))
242 cmd
.append('--config=%s/behat/behat.yml' %
(M
.get('behat_dataroot')))
244 # Checking feature argument
246 filepath
= args
.feature
247 if not filepath
.startswith('/'):
248 filepath
= os
.path
.join(M
.get('path'), filepath
)
253 phpCommand
= '%s -S localhost:8000' %
(self
.C
.get('php'))
254 seleniumCommand
= None
256 seleniumCommand
= '%s -jar %s' %
(self
.C
.get('java'), seleniumPath
)
258 olderThan26
= M
.branch_compare(26, '<')
261 logging
.info('Preparing Behat testing')
263 # Preparing PHP Server
265 if olderThan26
and not M
.get('behat_switchcompletely'):
266 logging
.info('Starting standalone PHP server')
268 kwargs
['cwd'] = M
.get('path')
269 phpServer
= ProcessInThread(phpCommand
, **kwargs
)
273 seleniumServer
= None
274 if seleniumPath
and not nojavascript
:
275 logging
.info('Starting Selenium server')
277 if args
.seleniumverbose
:
278 kwargs
['stdout'] = None
279 kwargs
['stderr'] = None
281 # Logging Selenium to a temporary file, this can be useful, and also it appears
282 # that Selenium hangs when stderr is not buffered.
283 fileOutPath
= os
.path
.join(gettempdir(), 'selenium_%s_out.log' %
(M
.get('identifier')))
284 fileErrPath
= os
.path
.join(gettempdir(), 'selenium_%s_err.log' %
(M
.get('identifier')))
285 tmpfileOut
= open(fileOutPath
, 'w')
286 tmpfileErr
= open(fileErrPath
, 'w')
287 logging
.debug('Logging Selenium output to: %s' %
(fileOutPath
))
288 logging
.debug('Logging Selenium errors to: %s' %
(fileErrPath
))
289 kwargs
['stdout'] = tmpfileOut
290 kwargs
['stderr'] = tmpfileErr
291 seleniumServer
= ProcessInThread(seleniumCommand
, **kwargs
)
292 seleniumServer
.start()
294 logging
.info('Running Behat tests')
296 # Sleep for a few seconds before starting Behat
297 if phpServer
or seleniumServer
:
298 launchSleep
= int(self
.C
.get('behat.launchSleep'))
299 logging
.debug('Waiting for %d seconds to allow Selenium and/or the PHP Server to start ' %
(launchSleep
))
305 logging
.info('More output can be found at:\n %s\n %s', outputDir
, outpurUrl
)
306 process(cmd
, M
.path
, None, None)
307 except KeyboardInterrupt:
310 # Kill the remaining processes
311 if phpServer
and phpServer
.is_alive():
313 if seleniumServer
and seleniumServer
.is_alive():
314 seleniumServer
.kill()
322 logging
.info('More output will be accessible at:\n %s\n %s', outputDir
, outpurUrl
)
324 logging
.info('Launch PHP Server (or set $CFG->behat_switchcompletely to True):\n %s' %
(phpCommand
))
326 logging
.info('Launch Selenium (optional):\n %s' %
(seleniumCommand
))
327 logging
.info('Launch Behat:\n %s' %
(cmd
))
329 except Exception as e
:
332 def disable(self
, M
):
333 logging
.info('Disabling Behat')
334 M
.cli('admin/tool/behat/cli/util.php', '--disable')
335 M
.removeConfig('behat_switchcompletely')