Monday, June 17, 2013

automating web tests with Ghost

Developing reliable software for the web is a challenging proposition. A site's code organization is complex, with libraries interacting with each other in an exponentially expanding number of possibilities.  It's quite easy to make a minor change in one part of the site that breaks another part.

Manual testing isn't an option. If you have a rarely-used but critical part of the site's workflow, like "email me my forgotten password", it would affect users if the functionality breaks. With manual testing you might never know your users are getting a poor experience.

One technique for making reliable web software is using automated tests.  In addition to the lower-level unit tests of code, it's valuable to have a scriptable "browser" that is able to click through your site and check various features.

Install and verify Ghost

Here's how to install and use Ghost, a Python library that emulates a Webkit browser. The browser is comparable to Apple Safari or Google Chrome.  Instructions are for Ubuntu.

sudo apt-get install python-pyside
sudo pip install Ghost.py

Verify that things were installed correctly:
python -c 'from ghost import Ghost'

Example headless web scripting

Here's a sample program.  It loads the Duck Duck Go search page, then types in a search query. It then clicks the submit button, waits for the search results to load, and prints out the HTML of the first result. Finally it captures a snapshot of the page!

#!/usr/bin/python
from ghost import Ghost

ghost = Ghost()
ghost.open('http://duckduckgo.com/')
 
ghost.wait_for_selector('input[name=q]')
ghost.fill("#search_form_homepage", {'q': 'beer'})
ghost.fire_on("#search_form_homepage",
              "submit",
              expect_loading=True)
 
ghost.wait_for_selector('#r1-0')
result, _resources = ghost.evaluate(
    "document.getElementById('r1-0').innerHTML;")
print result
ghost.capture_to('beer.png')


Here's the above in a Gist for your use: Ghost example with Duck Duck Go

Resources:






Wednesday, June 5, 2013

Retrotechtacular: How I wrote Pitfall for the Atari 2600

I adored writing assembly language for the Apple back in, err, 1984?  Somehow it felt very "clean", things would be completely inscrutable until you tweaked just the right bit and then everything would pop in to place.  Then the program was rock-solid, doing exactly what you told it incredibly efficiently and quickly.  Of course it never did exactly what you wanted, but that was the fun of it.

The "C" language had some of the clarity of assembly, and the advantage it would crash hard if you asked it to do something silly.  Writing a device driver in C++ (what luxury!) was fun because it would do different things depending on if I was writing debug statements or not.  The console was 9600 baud, and the code was timing-dependent, so writing code so you could see what you were doing... changed what you were doing!   As a perk, if you did something unexpected the machine would hard-lock, requiring a long and luxurious 40-second reboot.  What fun!

More common languages are too "fluffy", they always do things; there's no time consequence for writing loose code.  Ah well.  I adore Python and even though working with it for many years I still really enjoy it and learn new things.

I imagine this guy knows *exactly* what I'm talking about:
Retrotechtacular: How I wrote Pitfall for the Atari 2600

Monday, May 20, 2013

easy way to maintain source code decorum


It's best to maintain source code in a clean, pretty state. It's easy to read, and more importantly easy to find small code changes that might lead to dramatic effects.
But, during development, people add debugging statements. Print statements, debug logging, and things like "# XXX fix this".  Letting this code hit production is not good, as it confuses the normal operation of the system.

To pick bits of debugging fluff from your code, add this to your top-level Makefile.  Before merging your code with the main branch, run "make sniff" to sniff your code for unwantedly smelly bits, like leftover debugging code.

The following will only check newly-changed code, and won't warn on old smelly code:

sniff:
git diff develop | awk '/(debug|XXX)/ { print $$1; found=1; } END { exit found }'

If this is run as part of a Jenkins job it'll flag stinky code as an error. 

trivial Jenkins configuration

Knowing when your software breaks is useful. If you catch subtle errors soon after the change, it's *much* easier to figure out which bit of code "optimization" broke things for the users.

Another benefit is fixing "works for me" syndrome. Even if you're building software just for yourself, it's nice to have a 3rd party to verify that you didn't do silly things. Forgetting to check in South database changes is quite easy, but that simple omission will break everything.

Jenkins is a cranky to set up, because it's a collection of moving parts. The best setup would be having Jenkins rebuild your tests every time GitHub detects a source code change.

The following setup is not as geeky, but it's much, much easier: tell Jenkins copy local source files and test, every now and then.


  1. go to Jenkins on your local computer: http://localhost:8080/
  2. New Job ("free-style software project")
  3. You're now on the Configure page. click Add Build Step.
  4. Execute Shell
  5. adjust Makefile to your liking
  6. run the test every few minutes

For #4, in the Command area, type this little script:


rsync -av --delete --exclude='.git' MYSOURCEDIR $WORKSPACE
make -C $WORKSPACE test

Replace MYSOURCEDIR with your source directory -- the full path. For me it's /home/johnm/work/yed.   The above copies source from your normal development area to the job-specific Jenkins area.  It strips files that have been deleted in the source tree

In step #5, put a Makefile with a "test" verb in the top of your source code. I'm running Django, so to test the project I want to run the Django test suite on my main app.


test:
./manage.py test -v1

Remember to use the TAB key instead of spaces on the 2nd line.

At this stage, test your Jenkins job.  On the Job screen click Configure on the left to return to the Configuration page.  Edit the job, but don't press Save at the bottom of the page -- press Apply.  Open your job in a second browser window.  Click Build Now to start the job, wait for a few seconds, then click the new link which appears in the Build History section of the page.

If it's not perfect, go back to the Configuration page, make changes, press Apply; switch to the Project browser window, click Build Now again.  This workflow lets you rapidly make changes to your Jenkins project without having lots of windows open.


Step 6: Once #1-5 above is working to your satisfaction, you can automate the test.  On the Job Configure screen, in the Build Triggers section, enable "Build periodically".  In the text area, type this schedule:

H/15 * * * *

This means run your test every 15 minutes, no matter if anything has changed or not.  The "H" is to not overwhelm your system if lots of tests are running at once.



As an alternate for #5, just to make sure things are running, you can use the following Makefile instead:

test:
echo 'out of beer!' ; false

When you run the Jenkins job on this, the test will fail because the "false" command returns a status of 1. This proves that Jenkins sees your code, is copying to the right place, and is running your Makefile test command.



Friday, April 26, 2013

oooo: jQuery annotated source

Being able to easily read library source code is a very wonderful thing. I used this for years to become a better Python programmer.  The Twisted library is a bear, but the code is beautiful and clear.  Here's an annotated version of jQuery:

robflaherty/jquery-annotated-source @ GitHub

Tuesday, April 23, 2013

interesting database performance article

Postgres doesn't support that much compression directly. These guys decided to take the performance hit of compressing/decompressing things at the filesystem layer... but it's actually *faster*. Turns out I/O bound stuff like the database, even if you spend more CPU on compression, there are multiplicative effects if the data is smaller.

http://citusdata.com/blog/64-zfs-compression