Continuous Integration
Testing with Hudson
Wednesday, October 27, 2010
Continuous Integration
Wednesday, October 27, 2010
Continuous Integration
• Extreme Programming principle
• Martin Fowler
http://www.martinfowler.com/articles/
continuousInt...
Continuous Integration
• Maintain a Source
Repository
• Automate the Build
• Make the Build Self-
Testing
• Everyone Commi...
Essentially
• Commit your code
• Test the crap out of it
• Regressions are bad
• Assign blame
• Shame is a great motivator...
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Hudson vs Buildbot?
Wednesday, October 27, 2010
*shrug*
Wednesday, October 27, 2010
Hudson (Pros)
Wednesday, October 27, 2010
Easy to install/run
%> java -jar hudson.war
Wednesday, October 27, 2010
Easy to Configure
• Create
• Modify
• Manage
• All TTW
Wednesday, October 27, 2010
Frequent Releases
~ 1 / Week
Wednesday, October 27, 2010
Inline Documentation
Wednesday, October 27, 2010
Plugins
Wednesday, October 27, 2010
Hudson (Cons)
Wednesday, October 27, 2010
It’s Java.
Wednesday, October 27, 2010
It’s Java.
1. It eats resources
2. I don’t know how to write plugins
Wednesday, October 27, 2010
Hudson
Wednesday, October 27, 2010
Project Layout
Wednesday, October 27, 2010
Project Layout
• Project/Job
Wednesday, October 27, 2010
Project Layout
• Project/Job
• Build
Wednesday, October 27, 2010
Project Layout
• Project
• Build
• Trigger
Wednesday, October 27, 2010
Triggers
• Other projects
• Remote trigger
• Periodically
• SCM polling
Wednesday, October 27, 2010
Project Layout
• Project/Job
• Build
• Trigger
• Build Step
Wednesday, October 27, 2010
Build Steps
• Shell script
• Python script
• Ant
• Maven
Wednesday, October 27, 2010
Project Layout
• Project/Job
• Build
• Trigger
• Build Step
• Post-Build Action
Wednesday, October 27, 2010
Post-Build Actions
• Trigger builds
• Archive results
• Generate reports
• Send email
Wednesday, October 27, 2010
Build Status
Wednesday, October 27, 2010
Build Status
• Successful
• Unstable
• Failed
Wednesday, October 27, 2010
Build Stability
Wednesday, October 27, 2010
Filesystem Layout
Wednesday, October 27, 2010
Filesystem Layout
hudson /
config.xml
hudson.plugins.*.xml
jobs /
myjob /
builds /
2010-10-21_11-56-40
2010-10-21_12-20-26
...
Filesystem Layout
hudson /
config.xml
hudson.plugins.*.xml
jobs /
myjob /
builds /
2010-10-21_11-56-40
2010-10-21_12-20-26
...
Filesystem Layout
hudson /
config.xml
hudson.plugins.*.xml
jobs /
myjob /
builds /
2010-10-21_11-56-40
2010-10-21_12-20-26
...
Filesystem Layout
hudson /
config.xml
hudson.plugins.*.xml
jobs /
myjob /
builds /
2010-10-21_11-56-40
2010-10-21_12-20-26
...
Plugins
Wednesday, October 27, 2010
Plugins
• Green Balls
• Python
• Subversion
• Trac
• Warnings
• Violations
• Cobertura
• Disk Usage
• Chuck Norris
Wednesd...
Plugins
• Green Balls
• Python
• Subversion
• Trac
• Warnings
• Violations
• Cobertura
• Disk Usage
• Chuck Norris
Wednesd...
Plone Integration
Wednesday, October 27, 2010
Buildout &
mr.developer
Wednesday, October 27, 2010
buildout.cfg
[buildout]
...
extensions =
mr.developer
Wednesday, October 27, 2010
buildout.cfg
[buildout]
...
extensions =
mr.developer
sources = sources
[sources]
fsd.core = svn https:.../weblion/fsd.cor...
mr.developer
• bin/develop checkout
• bin/develop status
• bin/develop update
• bin/develop activate (deactivate)
• bin/de...
buildout.cfg
[buildout]
...
extensions =
mr.developer
sources = sources
auto-checkout =
fsd.core
fsd.membrane
[sources]
fs...
buildout
bin/
buildout
develop
...
bootstrap.py
buildout.cfg
...
src/
fsd.core
fsd.membrane
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Tests
Wednesday, October 27, 2010
Tests
• bin/test returns text
• Hudson wants JUnit (XML) format
• collective.xmltestreport
Wednesday, October 27, 2010
hudson.cfg
[buildout]
extends = buildout.cfg
parts +=
xmltestrunner
[xmltestrunner]
recipe = collective.xmltestreport
eggs...
hudson.cfg
[buildout]
extends = buildout.cfg
parts +=
xmltestrunner
[xmltestrunner]
recipe = collective.xmltestreport
eggs...
hudson.cfg
[buildout]
extends = buildout.cfg
parts +=
xmltestrunner
[xmltestrunner]
recipe = collective.xmltestreport
eggs...
Wednesday, October 27, 2010
Test Coverage
Wednesday, October 27, 2010
Test Coverage
[coverage]
recipe = zc.recipe.egg
eggs = coverage
initialization =
sys.argv = sys.argv[:] + ['run',
'bin/xml...
Test Coverage
[report]
recipe = zc.recipe.egg
eggs = coverage
scripts = coverage=report
initialization =
eggs = '${buildou...
Test Coverage
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Code Analysis
Wednesday, October 27, 2010
Code Analysis
• zptlint
• pyflakes
• pylint
• jslint
Wednesday, October 27, 2010
zptlint
Wednesday, October 27, 2010
zptlint
• http://pypi.python.org/pypi/zptlint
• Scan page templates for parser and output
errors
Wednesday, October 27, 20...
zptlint
[zptlint]
recipe = zc.recipe.egg
eggs = zptlint
entry-points = zptlint=zptlint:run
Wednesday, October 27, 2010
zptlint
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:meta...
zptlint
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:meta...
zptlint
%> bin/zptlint src/.../view.pt
Wednesday, October 27, 2010
zptlint
%> bin/zptlint src/.../view.pt
*** Error in: src/.../view.pt
<class 'zope.tal.taldefs.TALError'>: TAL attributes o...
Parsing Log Messages
• Warnings plug-in
• Regular expression
• Groovy script (groovy.codehaus.org)
Wednesday, October 27, ...
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Priorities
• HIGH
• NORMAL
• LOW
Wednesday, October 27, 2010
zptlint
[zptlint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -e zptlint.log ]; then
echo "Old...
zptlint
[zptlint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -e zptlint.log ]; then
echo "Old...
zptlint
[zptlint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -e zptlint.log ]; then
echo "Old...
zptlint
[zptlint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -e zptlint.log ]; then
echo "Old...
zptlint
[zptlint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -e zptlint.log ]; then
echo "Old...
zptlint
[buildout]
...
# A list of package locations to be examined
# by Hudson
package-directories =
src/fsd.core/fsd/cor...
zptlint
[zptlint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -e zptlint.log ]; then
echo "Old...
zptlint
[zptlint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -e zptlint.log ]; then
echo "Old...
zptlint
[zptlint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -e zptlint.log ]; then
echo "Old...
zptlint
[zptlint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -e zptlint.log ]; then
echo "Old...
zptlint
[zptlint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -e zptlint.log ]; then
echo "Old...
zptlint
[zptlint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -e zptlint.log ]; then
echo "Old...
zptlint
[zptlint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -e zptlint.log ]; then
echo "Old...
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
pyflakes
Wednesday, October 27, 2010
pyflakes
• Python syntax checking
• Find common errors quickly
• Doesn’t attempt to run Python code
Wednesday, October 27, ...
pyflakes
[pyflakes]
recipe = zc.recipe.egg
eggs = pyflakes
entry-points = pyflakes=pyflakes.scripts.pyflakes:main
Wednesday...
pyflakes
%> bin/pyflakes src/.../person.py
Wednesday, October 27, 2010
pyflakes
%> bin/pyflakes src/.../person.py
src/.../person.py:25: 'ATTRIBUTE_NAME' imported but unused
src/.../person.py:25:...
pyflakes
[pyflakes-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -s pyflakes.log ]; then
rm pyfl...
pyflakes
[pyflakes-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -s pyflakes.log ]; then
rm pyfl...
pyflakes
[pyflakes-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -s pyflakes.log ]; then
rm pyfl...
pyflakes
[pyflakes-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -s pyflakes.log ]; then
rm pyfl...
pyflakes
[pyflakes-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -s pyflakes.log ]; then
rm pyfl...
[pyflakes-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -s pyflakes.log ]; then
rm pyflakes.log...
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
pylint
Wednesday, October 27, 2010
pylint
• Syntax checking
• PEP8 compliance
• Imports code
• You’ve done everything wrong
Wednesday, October 27, 2010
pylint
[pylint]
recipe = zc.recipe.egg
eggs = logilab.pylintinstaller
extra-paths = ${instance:location}/lib/python
entry-...
pylint
• disable-msg
• R0911:Too many return statements (%s/%s)
• R0201: Method could be a function
• W0201:Attribute %r d...
pylint
[pylint-test]
recipe = collective.recipe.template
input = inline:
#!/bin/sh
if [ -s pylint.log ]; then
rm pylint.lo...
pylint
Wednesday, October 27, 2010
pylint
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Selenium
Wednesday, October 27, 2010
Selenium
• Automated web application testing
Wednesday, October 27, 2010
Selenium
1. Open this page
2. Click that thing
3. Fill out that form
4. Click submit
Did it work?
Wednesday, October 27, 2...
Selenium Testcase
def test_login_overlay(self):
self.open("/")
self.wait()
self.selenium.click('link=Log in')
self.waitFor...
Selenium Testcase
class SeleniumLayer(PloneSandboxLayer):
defaultBases = (HOST_ADJUSTABLE_ZSERVER_FIXTURE, PLONE_FIXTURE)
...
Selenium Testcase
class SeleniumLayer(PloneSandboxLayer):
defaultBases = (HOST_ADJUSTABLE_ZSERVER_FIXTURE, PLONE_FIXTURE)
...
Selenium Testcase
class SeleniumLayer(PloneSandboxLayer):
defaultBases = (HOST_ADJUSTABLE_ZSERVER_FIXTURE, PLONE_FIXTURE)
...
Selenium RC
• Firefox
• Internet Explorer
• Safari
• Chrome
• Opera
• more...
Wednesday, October 27, 2010
Selenium Grid
Wednesday, October 27, 2010
Wednesday, October 27, 2010
“Firefox?”
Wednesday, October 27, 2010
Thanks!
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Selenium Test Runner
fsd/
core/
...
selenium/
base.py
testSelenium.py
tests/
...
Wednesday, October 27, 2010
Selenium Test Runner
[selenium]
recipe = collective.xmltestreport
eggs =
fsd.core [test]
fsd.membrane [test]
defaults = ['...
Selenium Test Runner
[selenium]
recipe = collective.xmltestreport
eggs =
fsd.core [test]
fsd.membrane [test]
defaults = ['...
Selenium Test Runner
[selenium-firefox]
recipe = ${selenium:recipe}
eggs = ${selenium:eggs}
defaults = ${selenium:defaults...
Selenium Test Runner
%> bin/selenium-firefox
%> bin/selenium-safari
Wednesday, October 27, 2010
Selenium Test Runner
workspace/
bin/
...
parts/
selenium-firefox/
testreports/
*.xml
selenium-safari/
testreports/
*.xml
W...
~
Wednesday, October 27, 2010
Sprint!
• Saturday
• QA Sprint
• Selenium testing and more
• Free food!
Wednesday, October 27, 2010
Multi-Configuration
Projects
Wednesday, October 27, 2010
Multi-Configuration
Projects
• Run a build for each set of n x n ( x n x n)
variables.
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Wish List
Wednesday, October 27, 2010
Wish List
• Load testing
• Sphinx builds
• Chameleon compatibility checking
• Windmill
Wednesday, October 27, 2010
Best Practices
Wednesday, October 27, 2010
Check inYour Configs
• Configuration is stored in XML files
• Easy to back up to SVN
Wednesday, October 27, 2010
Check inYour Configs
hudson /
config.xml
hudson.plugins.*.xml
jobs /
myjob /
builds /
2010-10-21_11-56-40
2010-10-21_12-20-2...
hudson /
config.xml
hudson.plugins.*.xml
jobs /
myjob /
builds /
2010-10-21_11-56-40
2010-10-21_12-20-26
config.xml
workspac...
Check inYour Configs
Wednesday, October 27, 2010
WatchYour Disk Space
• “Hudson disk-usage” plugin
Wednesday, October 27, 2010
WatchYour Disk Space
Wednesday, October 27, 2010
WatchYour Disk Space
Wednesday, October 27, 2010
Wednesday, October 27, 2010
Use an Egg Cache
• Set up a default.cfg for your hudson
account.
[buildout]
eggs-directory =
/home/hudson/.buildout/eggs
d...
Pin unittest2
• (If you’re using it)
• plone.app.testing
• 3x faster buildout
Wednesday, October 27, 2010
Split Big Builds
• How long does the build take?
• How often does each part need to be run?
• Tests?
• Coverage?
Wednesday...
Split Big Builds
• Build
• Test
• Syntax Checking
• Coverage
• Selenium
Wednesday, October 27, 2010
Split Big Builds
1. “Build other projects” post-build action
Wednesday, October 27, 2010
Split Big Builds
1. “Build other projects” post-build action
2. Custom workspace
Wednesday, October 27, 2010
Split Big Builds
1. “Build other projects” post-build action
2. Custom workspace
3. Build Fingerprinting
Wednesday, Octobe...
Fingerprinting
• “These builds are related”
• Archive an md5 hash of a file to act as an
identifier
Wednesday, October 27, 2...
Fingerprinting
In parent build:
In parent and child builds:
Wednesday, October 27, 2010
Reporting
Wednesday, October 27, 2010
Hudson ReportViews
Wednesday, October 27, 2010
Email
Wednesday, October 27, 2010
Chuck
Wednesday, October 27, 2010
iPhone
Wednesday, October 27, 2010
Extreme Feedback
Devices
Wednesday, October 27, 2010
hudson.plone.org
• Currently builds Plone 4.0.x
• Core developers have access
• Add your core & collective projects
Wednes...
Thanks
• Martin Aspeli
• Timo Stollenwerk
• Hanno Schlichting
Wednesday, October 27, 2010
QA Sprint!
• Saturday
• QA Sprint
• Selenium testing and more
• Free food!
Wednesday, October 27, 2010
Upcoming SlideShare
Loading in...5
×

Continuous Integration Testing for Plone Using Hudson

1,823

Published on

http://db.tt/pdKBTHC

Published in: Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,823
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
51
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Transcript of "Continuous Integration Testing for Plone Using Hudson"

  1. 1. Continuous Integration Testing with Hudson Wednesday, October 27, 2010
  2. 2. Continuous Integration Wednesday, October 27, 2010
  3. 3. Continuous Integration • Extreme Programming principle • Martin Fowler http://www.martinfowler.com/articles/ continuousIntegration.html Wednesday, October 27, 2010
  4. 4. Continuous Integration • Maintain a Source Repository • Automate the Build • Make the Build Self- Testing • Everyone Commits Every Day • Every Commit Should Be Built • Keep the Build Fast • Test in a Clone of the Production Environment • Make it Easy to Get the Latest Deliverables • Everyone Can See the Results of the Latest Build • Automate Deployment Wednesday, October 27, 2010
  5. 5. Essentially • Commit your code • Test the crap out of it • Regressions are bad • Assign blame • Shame is a great motivator Wednesday, October 27, 2010
  6. 6. Wednesday, October 27, 2010
  7. 7. Wednesday, October 27, 2010
  8. 8. Hudson vs Buildbot? Wednesday, October 27, 2010
  9. 9. *shrug* Wednesday, October 27, 2010
  10. 10. Hudson (Pros) Wednesday, October 27, 2010
  11. 11. Easy to install/run %> java -jar hudson.war Wednesday, October 27, 2010
  12. 12. Easy to Configure • Create • Modify • Manage • All TTW Wednesday, October 27, 2010
  13. 13. Frequent Releases ~ 1 / Week Wednesday, October 27, 2010
  14. 14. Inline Documentation Wednesday, October 27, 2010
  15. 15. Plugins Wednesday, October 27, 2010
  16. 16. Hudson (Cons) Wednesday, October 27, 2010
  17. 17. It’s Java. Wednesday, October 27, 2010
  18. 18. It’s Java. 1. It eats resources 2. I don’t know how to write plugins Wednesday, October 27, 2010
  19. 19. Hudson Wednesday, October 27, 2010
  20. 20. Project Layout Wednesday, October 27, 2010
  21. 21. Project Layout • Project/Job Wednesday, October 27, 2010
  22. 22. Project Layout • Project/Job • Build Wednesday, October 27, 2010
  23. 23. Project Layout • Project • Build • Trigger Wednesday, October 27, 2010
  24. 24. Triggers • Other projects • Remote trigger • Periodically • SCM polling Wednesday, October 27, 2010
  25. 25. Project Layout • Project/Job • Build • Trigger • Build Step Wednesday, October 27, 2010
  26. 26. Build Steps • Shell script • Python script • Ant • Maven Wednesday, October 27, 2010
  27. 27. Project Layout • Project/Job • Build • Trigger • Build Step • Post-Build Action Wednesday, October 27, 2010
  28. 28. Post-Build Actions • Trigger builds • Archive results • Generate reports • Send email Wednesday, October 27, 2010
  29. 29. Build Status Wednesday, October 27, 2010
  30. 30. Build Status • Successful • Unstable • Failed Wednesday, October 27, 2010
  31. 31. Build Stability Wednesday, October 27, 2010
  32. 32. Filesystem Layout Wednesday, October 27, 2010
  33. 33. Filesystem Layout hudson / config.xml hudson.plugins.*.xml jobs / myjob / builds / 2010-10-21_11-56-40 2010-10-21_12-20-26 config.xml workspace / Wednesday, October 27, 2010
  34. 34. Filesystem Layout hudson / config.xml hudson.plugins.*.xml jobs / myjob / builds / 2010-10-21_11-56-40 2010-10-21_12-20-26 config.xml workspace / Wednesday, October 27, 2010
  35. 35. Filesystem Layout hudson / config.xml hudson.plugins.*.xml jobs / myjob / builds / 2010-10-21_11-56-40 2010-10-21_12-20-26 config.xml workspace / Wednesday, October 27, 2010
  36. 36. Filesystem Layout hudson / config.xml hudson.plugins.*.xml jobs / myjob / builds / 2010-10-21_11-56-40 2010-10-21_12-20-26 config.xml workspace / Wednesday, October 27, 2010
  37. 37. Plugins Wednesday, October 27, 2010
  38. 38. Plugins • Green Balls • Python • Subversion • Trac • Warnings • Violations • Cobertura • Disk Usage • Chuck Norris Wednesday, October 27, 2010
  39. 39. Plugins • Green Balls • Python • Subversion • Trac • Warnings • Violations • Cobertura • Disk Usage • Chuck Norris Wednesday, October 27, 2010
  40. 40. Plone Integration Wednesday, October 27, 2010
  41. 41. Buildout & mr.developer Wednesday, October 27, 2010
  42. 42. buildout.cfg [buildout] ... extensions = mr.developer Wednesday, October 27, 2010
  43. 43. buildout.cfg [buildout] ... extensions = mr.developer sources = sources [sources] fsd.core = svn https:.../weblion/fsd.core/trunk fsd.membrane = svn https:.../weblion/fsd.membrane/trunk Wednesday, October 27, 2010
  44. 44. mr.developer • bin/develop checkout • bin/develop status • bin/develop update • bin/develop activate (deactivate) • bin/develop reset • bin/develop purge Wednesday, October 27, 2010
  45. 45. buildout.cfg [buildout] ... extensions = mr.developer sources = sources auto-checkout = fsd.core fsd.membrane [sources] fsd.core = svn https:.../weblion/fsd.core/trunk fsd.membrane = svn https:.../weblion/fsd.membrane/trunk Wednesday, October 27, 2010
  46. 46. buildout bin/ buildout develop ... bootstrap.py buildout.cfg ... src/ fsd.core fsd.membrane Wednesday, October 27, 2010
  47. 47. Wednesday, October 27, 2010
  48. 48. Wednesday, October 27, 2010
  49. 49. Tests Wednesday, October 27, 2010
  50. 50. Tests • bin/test returns text • Hudson wants JUnit (XML) format • collective.xmltestreport Wednesday, October 27, 2010
  51. 51. hudson.cfg [buildout] extends = buildout.cfg parts += xmltestrunner [xmltestrunner] recipe = collective.xmltestreport eggs = ${test:eggs} defaults = ${test:defaults} + ['--xml'] Wednesday, October 27, 2010
  52. 52. hudson.cfg [buildout] extends = buildout.cfg parts += xmltestrunner [xmltestrunner] recipe = collective.xmltestreport eggs = ${test:eggs} defaults = ${test:defaults} + ['--xml'] Wednesday, October 27, 2010
  53. 53. hudson.cfg [buildout] extends = buildout.cfg parts += xmltestrunner [xmltestrunner] recipe = collective.xmltestreport eggs = ${test:eggs} defaults = ${test:defaults} + ['--xml'] Wednesday, October 27, 2010
  54. 54. Wednesday, October 27, 2010
  55. 55. Test Coverage Wednesday, October 27, 2010
  56. 56. Test Coverage [coverage] recipe = zc.recipe.egg eggs = coverage initialization = sys.argv = sys.argv[:] + ['run', 'bin/xmltestrunner', '-k', '-q', '--xml'] Wednesday, October 27, 2010
  57. 57. Test Coverage [report] recipe = zc.recipe.egg eggs = coverage scripts = coverage=report initialization = eggs = '${buildout:eggs-directory}' bin = '${buildout:directory}/bin' exclude = '--omit=' + ','.join([eggs, sys.prefix, bin]) sys.argv = sys.argv[:] + ['xml', '-i', exclude, include] Wednesday, October 27, 2010
  58. 58. Test Coverage Wednesday, October 27, 2010
  59. 59. Wednesday, October 27, 2010
  60. 60. Wednesday, October 27, 2010
  61. 61. Wednesday, October 27, 2010
  62. 62. Wednesday, October 27, 2010
  63. 63. Code Analysis Wednesday, October 27, 2010
  64. 64. Code Analysis • zptlint • pyflakes • pylint • jslint Wednesday, October 27, 2010
  65. 65. zptlint Wednesday, October 27, 2010
  66. 66. zptlint • http://pypi.python.org/pypi/zptlint • Scan page templates for parser and output errors Wednesday, October 27, 2010
  67. 67. zptlint [zptlint] recipe = zc.recipe.egg eggs = zptlint entry-points = zptlint=zptlint:run Wednesday, October 27, 2010
  68. 68. zptlint <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal" lang="en" metal:use-macro="context/main_template/macros/master"> <body> <metal:fill fill-slot="content-core"> <div tal:content="structure view/context/text/output"> <h2>People</h2> <div tal:content="structure context/@@people" /> </metal:fill> </body> </html> Wednesday, October 27, 2010
  69. 69. zptlint <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal" lang="en" metal:use-macro="context/main_template/macros/master"> <body> <metal:fill fill-slot="content-core"> <div tal:content="structure view/context/text/output"> <h2>People</h2> <div tal:content="structure context/@@people" /> </metal:fill> </body> </html> Wednesday, October 27, 2010
  70. 70. zptlint %> bin/zptlint src/.../view.pt Wednesday, October 27, 2010
  71. 71. zptlint %> bin/zptlint src/.../view.pt *** Error in: src/.../view.pt <class 'zope.tal.taldefs.TALError'>: TAL attributes on <div> require explicit </div> , at line 12, column 9 Wednesday, October 27, 2010
  72. 72. Parsing Log Messages • Warnings plug-in • Regular expression • Groovy script (groovy.codehaus.org) Wednesday, October 27, 2010
  73. 73. Wednesday, October 27, 2010
  74. 74. Wednesday, October 27, 2010
  75. 75. Wednesday, October 27, 2010
  76. 76. Wednesday, October 27, 2010
  77. 77. Wednesday, October 27, 2010
  78. 78. Wednesday, October 27, 2010
  79. 79. Wednesday, October 27, 2010
  80. 80. Priorities • HIGH • NORMAL • LOW Wednesday, October 27, 2010
  81. 81. zptlint [zptlint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -e zptlint.log ]; then echo "Old zptlint.log file removed" rm zptlint.log fi echo "Running zptlint-test" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find $pkg -regex ".*.[c|z]?pt" | xargs -r bin/zptlint | perl -p -e 's/s+$/ /g;s/s+/ /g;s/*{3}s?/n/g' >> zptlint.log done output = ${buildout:directory}/bin/zptlint-test mode = 755 Wednesday, October 27, 2010
  82. 82. zptlint [zptlint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -e zptlint.log ]; then echo "Old zptlint.log file removed" rm zptlint.log fi echo "Running zptlint-test" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find $pkg -regex ".*.[c|z]?pt" | xargs -r bin/zptlint | perl -p -e 's/s+$/ /g;s/s+/ /g;s/*{3}s?/n/g' >> zptlint.log done output = ${buildout:directory}/bin/zptlint-test mode = 755 Wednesday, October 27, 2010
  83. 83. zptlint [zptlint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -e zptlint.log ]; then echo "Old zptlint.log file removed" rm zptlint.log fi echo "Running zptlint-test" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find $pkg -regex ".*.[c|z]?pt" | xargs -r bin/zptlint | perl -p -e 's/s+$/ /g;s/s+/ /g;s/*{3}s?/n/g' >> zptlint.log done output = ${buildout:directory}/bin/zptlint-test mode = 755 Wednesday, October 27, 2010
  84. 84. zptlint [zptlint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -e zptlint.log ]; then echo "Old zptlint.log file removed" rm zptlint.log fi echo "Running zptlint-test" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find $pkg -regex ".*.[c|z]?pt" | xargs -r bin/zptlint | perl -p -e 's/s+$/ /g;s/s+/ /g;s/*{3}s?/n/g' >> zptlint.log done output = ${buildout:directory}/bin/zptlint-test mode = 755 Wednesday, October 27, 2010
  85. 85. zptlint [zptlint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -e zptlint.log ]; then echo "Old zptlint.log file removed" rm zptlint.log fi echo "Running zptlint-test" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find $pkg -regex ".*.[c|z]?pt" | xargs -r bin/zptlint | perl -p -e 's/s+$/ /g;s/s+/ /g;s/*{3}s?/n/g' >> zptlint.log done output = ${buildout:directory}/bin/zptlint-test mode = 755 Wednesday, October 27, 2010
  86. 86. zptlint [buildout] ... # A list of package locations to be examined # by Hudson package-directories = src/fsd.core/fsd/core src/fsd.membrane/fsd/membrane Wednesday, October 27, 2010
  87. 87. zptlint [zptlint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -e zptlint.log ]; then echo "Old zptlint.log file removed" rm zptlint.log fi echo "Running zptlint-test" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find $pkg -regex ".*.[c|z]?pt" | xargs -r bin/zptlint | perl -p -e 's/s+$/ /g;s/s+/ /g;s/*{3}s?/n/g' >> zptlint.log done output = ${buildout:directory}/bin/zptlint-test mode = 755 Wednesday, October 27, 2010
  88. 88. zptlint [zptlint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -e zptlint.log ]; then echo "Old zptlint.log file removed" rm zptlint.log fi echo "Running zptlint-test" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find $pkg -regex ".*.[c|z]?pt" | xargs -r bin/zptlint | perl -p -e 's/s+$/ /g;s/s+/ /g;s/*{3}s?/n/g' >> zptlint.log done output = ${buildout:directory}/bin/zptlint-test mode = 755 Wednesday, October 27, 2010
  89. 89. zptlint [zptlint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -e zptlint.log ]; then echo "Old zptlint.log file removed" rm zptlint.log fi echo "Running zptlint-test" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find $pkg -regex ".*.[c|z]?pt" | xargs -r bin/zptlint | perl -p -e 's/s+$/ /g;s/s+/ /g;s/*{3}s?/n/g' >> zptlint.log done output = ${buildout:directory}/bin/zptlint-test mode = 755 Wednesday, October 27, 2010
  90. 90. zptlint [zptlint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -e zptlint.log ]; then echo "Old zptlint.log file removed" rm zptlint.log fi echo "Running zptlint-test" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find $pkg -regex ".*.[c|z]?pt" | xargs -r bin/zptlint | perl -p -e 's/s+$/ /g;s/s+/ /g;s/*{3}s?/n/g' >> zptlint.log done output = ${buildout:directory}/bin/zptlint-test mode = 755 Wednesday, October 27, 2010
  91. 91. zptlint [zptlint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -e zptlint.log ]; then echo "Old zptlint.log file removed" rm zptlint.log fi echo "Running zptlint-test" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find $pkg -regex ".*.[c|z]?pt" | xargs -r bin/zptlint | perl -p -e 's/s+$/ /g;s/s+/ /g;s/*{3}s?/n/g' >> zptlint.log done output = ${buildout:directory}/bin/zptlint-test mode = 755 Wednesday, October 27, 2010
  92. 92. zptlint [zptlint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -e zptlint.log ]; then echo "Old zptlint.log file removed" rm zptlint.log fi echo "Running zptlint-test" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find $pkg -regex ".*.[c|z]?pt" | xargs -r bin/zptlint | perl -p -e 's/s+$/ /g;s/s+/ /g;s/*{3}s?/n/g' >> zptlint.log done output = ${buildout:directory}/bin/zptlint-test mode = 755 Wednesday, October 27, 2010
  93. 93. zptlint [zptlint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -e zptlint.log ]; then echo "Old zptlint.log file removed" rm zptlint.log fi echo "Running zptlint-test" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find $pkg -regex ".*.[c|z]?pt" | xargs -r bin/zptlint | perl -p -e 's/s+$/ /g;s/s+/ /g;s/*{3}s?/n/g' >> zptlint.log done output = ${buildout:directory}/bin/zptlint-test mode = 755 Wednesday, October 27, 2010
  94. 94. Wednesday, October 27, 2010
  95. 95. Wednesday, October 27, 2010
  96. 96. Wednesday, October 27, 2010
  97. 97. Wednesday, October 27, 2010
  98. 98. pyflakes Wednesday, October 27, 2010
  99. 99. pyflakes • Python syntax checking • Find common errors quickly • Doesn’t attempt to run Python code Wednesday, October 27, 2010
  100. 100. pyflakes [pyflakes] recipe = zc.recipe.egg eggs = pyflakes entry-points = pyflakes=pyflakes.scripts.pyflakes:main Wednesday, October 27, 2010
  101. 101. pyflakes %> bin/pyflakes src/.../person.py Wednesday, October 27, 2010
  102. 102. pyflakes %> bin/pyflakes src/.../person.py src/.../person.py:25: 'ATTRIBUTE_NAME' imported but unused src/.../person.py:25: 'IAttributeUUID' imported but unused Wednesday, October 27, 2010
  103. 103. pyflakes [pyflakes-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -s pyflakes.log ]; then rm pyflakes.log echo "Old pyflakes.log file removed" fi echo "Running pyflakes" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find -L $pkg -regex ".*.py"|xargs -r bin/pyflakes >> pyflakes.log done output = ${buildout:directory}/bin/pyflakes-test mode = 755 Wednesday, October 27, 2010
  104. 104. pyflakes [pyflakes-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -s pyflakes.log ]; then rm pyflakes.log echo "Old pyflakes.log file removed" fi echo "Running pyflakes" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find -L $pkg -regex ".*.py"|xargs -r bin/pyflakes >> pyflakes.log done output = ${buildout:directory}/bin/pyflakes-test mode = 755 Wednesday, October 27, 2010
  105. 105. pyflakes [pyflakes-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -s pyflakes.log ]; then rm pyflakes.log echo "Old pyflakes.log file removed" fi echo "Running pyflakes" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find -L $pkg -regex ".*.py"|xargs -r bin/pyflakes >> pyflakes.log done output = ${buildout:directory}/bin/pyflakes-test mode = 755 Wednesday, October 27, 2010
  106. 106. pyflakes [pyflakes-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -s pyflakes.log ]; then rm pyflakes.log echo "Old pyflakes.log file removed" fi echo "Running pyflakes" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find -L $pkg -regex ".*.py"|xargs -r bin/pyflakes >> pyflakes.log done output = ${buildout:directory}/bin/pyflakes-test mode = 755 Wednesday, October 27, 2010
  107. 107. pyflakes [pyflakes-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -s pyflakes.log ]; then rm pyflakes.log echo "Old pyflakes.log file removed" fi echo "Running pyflakes" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find -L $pkg -regex ".*.py"|xargs -r bin/pyflakes >> pyflakes.log done output = ${buildout:directory}/bin/pyflakes-test mode = 755 Wednesday, October 27, 2010
  108. 108. [pyflakes-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -s pyflakes.log ]; then rm pyflakes.log echo "Old pyflakes.log file removed" fi echo "Running pyflakes" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find -L $pkg -regex ".*.py"|xargs -r bin/pyflakes >> pyflakes.log done output = ${buildout:directory}/bin/pyflakes-test mode = 755 pyflakes Wednesday, October 27, 2010
  109. 109. Wednesday, October 27, 2010
  110. 110. Wednesday, October 27, 2010
  111. 111. Wednesday, October 27, 2010
  112. 112. Wednesday, October 27, 2010
  113. 113. Wednesday, October 27, 2010
  114. 114. Wednesday, October 27, 2010
  115. 115. Wednesday, October 27, 2010
  116. 116. Wednesday, October 27, 2010
  117. 117. Wednesday, October 27, 2010
  118. 118. Wednesday, October 27, 2010
  119. 119. Wednesday, October 27, 2010
  120. 120. Wednesday, October 27, 2010
  121. 121. Wednesday, October 27, 2010
  122. 122. Wednesday, October 27, 2010
  123. 123. Wednesday, October 27, 2010
  124. 124. pylint Wednesday, October 27, 2010
  125. 125. pylint • Syntax checking • PEP8 compliance • Imports code • You’ve done everything wrong Wednesday, October 27, 2010
  126. 126. pylint [pylint] recipe = zc.recipe.egg eggs = logilab.pylintinstaller extra-paths = ${instance:location}/lib/python entry-points = pylint=pylint.lint:Run arguments = [ '--output-format=parseable', '--zope=y', '--reports=y', '--disable-msg= E0611,F0401,W0232,E1101,C0103,C0111,R0201,W0201,R0911,R0904,F0220, E1103,R0901,E0211,E0213,E1002,W0622', '--generated-members=objects', ] + sys.argv[1:] Wednesday, October 27, 2010
  127. 127. pylint • disable-msg • R0911:Too many return statements (%s/%s) • R0201: Method could be a function • W0201:Attribute %r defined outside of __init__ • http://pylint-messages.wikidot.com/all-messages Wednesday, October 27, 2010
  128. 128. pylint [pylint-test] recipe = collective.recipe.template input = inline: #!/bin/sh if [ -s pylint.log ]; then rm pylint.log echo "Old pylint.log file removed" fi echo "Running pylint" PACKAGES="${buildout:package-directories}" for pkg in $PACKAGES do find -L $pkg -regex ".*.py" | xargs bin/pylint >> pylint.log done echo "Finished" output = ${buildout:directory}/bin/pylint-test mode = 755 Wednesday, October 27, 2010
  129. 129. pylint Wednesday, October 27, 2010
  130. 130. pylint Wednesday, October 27, 2010
  131. 131. Wednesday, October 27, 2010
  132. 132. Wednesday, October 27, 2010
  133. 133. Selenium Wednesday, October 27, 2010
  134. 134. Selenium • Automated web application testing Wednesday, October 27, 2010
  135. 135. Selenium 1. Open this page 2. Click that thing 3. Fill out that form 4. Click submit Did it work? Wednesday, October 27, 2010
  136. 136. Selenium Testcase def test_login_overlay(self): self.open("/") self.wait() self.selenium.click('link=Log in') self.waitForElement('form#login_form') self.selenium.type("name=__ac_name", TEST_USER_NAME) self.selenium.type("name=__ac_password", TEST_USER_PASSWORD) self.selenium.click("submit") self.wait() self.failUnless(self.selenium.is_text_present("Log out")) Wednesday, October 27, 2010
  137. 137. Selenium Testcase class SeleniumLayer(PloneSandboxLayer): defaultBases = (HOST_ADJUSTABLE_ZSERVER_FIXTURE, PLONE_FIXTURE) # Connection parameters seleniumHost = os.environ.get('SELENIUM_HOST', 'localhost') seleniumPort = os.environ.get('SELENIUM_PORT', '4444') seleniumBrowser = os.environ.get('SELENIUM_BROWSER', '*firefox') def setUpZope(self, app, configurationContext): ... def setUpPloneSite(self, portal): ... url = "http://%s:%s/%s" % (self['host'], self['port'], PLONE_SITE_ID) self['selenium'] = selenium.selenium(self.seleniumHost, self.seleniumPort, self.seleniumBrowser, url) self['selenium'].start() def tearDownPloneSite(self, portal): self['selenium'].stop() del self['selenium'] Wednesday, October 27, 2010
  138. 138. Selenium Testcase class SeleniumLayer(PloneSandboxLayer): defaultBases = (HOST_ADJUSTABLE_ZSERVER_FIXTURE, PLONE_FIXTURE) # Connection parameters seleniumHost = os.environ.get('SELENIUM_HOST', 'localhost') seleniumPort = os.environ.get('SELENIUM_PORT', '4444') seleniumBrowser = os.environ.get('SELENIUM_BROWSER', '*firefox') def setUpZope(self, app, configurationContext): ... def setUpPloneSite(self, portal): ... url = "http://%s:%s/%s" % (self['host'], self['port'], PLONE_SITE_ID) self['selenium'] = selenium.selenium(self.seleniumHost, self.seleniumPort, self.seleniumBrowser, url) self['selenium'].start() def tearDownPloneSite(self, portal): self['selenium'].stop() del self['selenium'] Wednesday, October 27, 2010
  139. 139. Selenium Testcase class SeleniumLayer(PloneSandboxLayer): defaultBases = (HOST_ADJUSTABLE_ZSERVER_FIXTURE, PLONE_FIXTURE) # Connection parameters seleniumHost = os.environ.get('SELENIUM_HOST', 'localhost') seleniumPort = os.environ.get('SELENIUM_PORT', '4444') seleniumBrowser = os.environ.get('SELENIUM_BROWSER', '*firefox') def setUpZope(self, app, configurationContext): ... def setUpPloneSite(self, portal): ... url = "http://%s:%s/%s" % (self['host'], self['port'], PLONE_SITE_ID) self['selenium'] = selenium.selenium(self.seleniumHost, self.seleniumPort, self.seleniumBrowser, url) self['selenium'].start() def tearDownPloneSite(self, portal): self['selenium'].stop() del self['selenium'] Wednesday, October 27, 2010
  140. 140. Selenium RC • Firefox • Internet Explorer • Safari • Chrome • Opera • more... Wednesday, October 27, 2010
  141. 141. Selenium Grid Wednesday, October 27, 2010
  142. 142. Wednesday, October 27, 2010
  143. 143. “Firefox?” Wednesday, October 27, 2010
  144. 144. Thanks! Wednesday, October 27, 2010
  145. 145. Wednesday, October 27, 2010
  146. 146. Selenium Test Runner fsd/ core/ ... selenium/ base.py testSelenium.py tests/ ... Wednesday, October 27, 2010
  147. 147. Selenium Test Runner [selenium] recipe = collective.xmltestreport eggs = fsd.core [test] fsd.membrane [test] defaults = ['--tests-pattern', '^f?selenium$', '--xml'] Wednesday, October 27, 2010
  148. 148. Selenium Test Runner [selenium] recipe = collective.xmltestreport eggs = fsd.core [test] fsd.membrane [test] defaults = ['--tests-pattern', '^f?selenium$', '--xml'] Wednesday, October 27, 2010
  149. 149. Selenium Test Runner [selenium-firefox] recipe = ${selenium:recipe} eggs = ${selenium:eggs} defaults = ${selenium:defaults} [selenium-safari] recipe = ${selenium:recipe} eggs = ${selenium:eggs} defaults = ${selenium:defaults} Wednesday, October 27, 2010
  150. 150. Selenium Test Runner %> bin/selenium-firefox %> bin/selenium-safari Wednesday, October 27, 2010
  151. 151. Selenium Test Runner workspace/ bin/ ... parts/ selenium-firefox/ testreports/ *.xml selenium-safari/ testreports/ *.xml Wednesday, October 27, 2010
  152. 152. ~ Wednesday, October 27, 2010
  153. 153. Sprint! • Saturday • QA Sprint • Selenium testing and more • Free food! Wednesday, October 27, 2010
  154. 154. Multi-Configuration Projects Wednesday, October 27, 2010
  155. 155. Multi-Configuration Projects • Run a build for each set of n x n ( x n x n) variables. Wednesday, October 27, 2010
  156. 156. Wednesday, October 27, 2010
  157. 157. Wish List Wednesday, October 27, 2010
  158. 158. Wish List • Load testing • Sphinx builds • Chameleon compatibility checking • Windmill Wednesday, October 27, 2010
  159. 159. Best Practices Wednesday, October 27, 2010
  160. 160. Check inYour Configs • Configuration is stored in XML files • Easy to back up to SVN Wednesday, October 27, 2010
  161. 161. Check inYour Configs hudson / config.xml hudson.plugins.*.xml jobs / myjob / builds / 2010-10-21_11-56-40 2010-10-21_12-20-26 config.xml workspace / Wednesday, October 27, 2010
  162. 162. hudson / config.xml hudson.plugins.*.xml jobs / myjob / builds / 2010-10-21_11-56-40 2010-10-21_12-20-26 config.xml workspace / Check inYour Configs Wednesday, October 27, 2010
  163. 163. Check inYour Configs Wednesday, October 27, 2010
  164. 164. WatchYour Disk Space • “Hudson disk-usage” plugin Wednesday, October 27, 2010
  165. 165. WatchYour Disk Space Wednesday, October 27, 2010
  166. 166. WatchYour Disk Space Wednesday, October 27, 2010
  167. 167. Wednesday, October 27, 2010
  168. 168. Use an Egg Cache • Set up a default.cfg for your hudson account. [buildout] eggs-directory = /home/hudson/.buildout/eggs download-cache = /home/hudson/.buildout/downloads Wednesday, October 27, 2010
  169. 169. Pin unittest2 • (If you’re using it) • plone.app.testing • 3x faster buildout Wednesday, October 27, 2010
  170. 170. Split Big Builds • How long does the build take? • How often does each part need to be run? • Tests? • Coverage? Wednesday, October 27, 2010
  171. 171. Split Big Builds • Build • Test • Syntax Checking • Coverage • Selenium Wednesday, October 27, 2010
  172. 172. Split Big Builds 1. “Build other projects” post-build action Wednesday, October 27, 2010
  173. 173. Split Big Builds 1. “Build other projects” post-build action 2. Custom workspace Wednesday, October 27, 2010
  174. 174. Split Big Builds 1. “Build other projects” post-build action 2. Custom workspace 3. Build Fingerprinting Wednesday, October 27, 2010
  175. 175. Fingerprinting • “These builds are related” • Archive an md5 hash of a file to act as an identifier Wednesday, October 27, 2010
  176. 176. Fingerprinting In parent build: In parent and child builds: Wednesday, October 27, 2010
  177. 177. Reporting Wednesday, October 27, 2010
  178. 178. Hudson ReportViews Wednesday, October 27, 2010
  179. 179. Email Wednesday, October 27, 2010
  180. 180. Chuck Wednesday, October 27, 2010
  181. 181. iPhone Wednesday, October 27, 2010
  182. 182. Extreme Feedback Devices Wednesday, October 27, 2010
  183. 183. hudson.plone.org • Currently builds Plone 4.0.x • Core developers have access • Add your core & collective projects Wednesday, October 27, 2010
  184. 184. Thanks • Martin Aspeli • Timo Stollenwerk • Hanno Schlichting Wednesday, October 27, 2010
  185. 185. QA Sprint! • Saturday • QA Sprint • Selenium testing and more • Free food! Wednesday, October 27, 2010
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×