From: <bms...@us...> - 2009-07-01 19:25:08
|
Revision: 2661 http://morphix.svn.sourceforge.net/morphix/?rev=2661&view=rev Author: bmsleight Date: 2009-07-01 19:24:57 +0000 (Wed, 01 Jul 2009) Log Message: ----------- Adding Autotesting2. Python, xml and joy Added Paths: ----------- trunk/mmaker/utils/autotesting2/ trunk/mmaker/utils/autotesting2/README trunk/mmaker/utils/autotesting2/autotesting.py trunk/mmaker/utils/autotesting2/tests/ trunk/mmaker/utils/autotesting2/tests/local-test_morphix-lightgui.xml trunk/mmaker/utils/autotesting2/tests/morphix-lightgui.xml Added: trunk/mmaker/utils/autotesting2/README =================================================================== --- trunk/mmaker/utils/autotesting2/README (rev 0) +++ trunk/mmaker/utils/autotesting2/README 2009-07-01 19:24:57 UTC (rev 2661) @@ -0,0 +1,99 @@ +AUTOTESTING README +================== + +autotesting --tests=TESTS.XML + +This readme outlines the different tags used in TESTS.XML. + +EXAMPLE TESTS.XML +================= + +<autotesting> + </tests> + <test> + <download>http://127.0.0.1/autotesting/debian-live-501-i386-xfce-desktop.iso</download> + <title>Debian Live (xfce)</title> + <description>Debian Live. Daily build of squeeze xfce iso.</description> + <background>http://git.debian.org/?p=debian-live/homepage.git;a=blob_plain;f=images/debian-live.png</background> + <frequency>daily</frequency> + <qemu> + <xscreen>800x600x24</xscreen> + <binary>qemu</binary> + <options>-cdrom</options> + <pause>10</pause> + <sendkeys>kp_enter</sendkeys> + <!-- Time to run qmeu --> + <time>600</time> + </qemu> + <output> + <!-- Number of test sets to keep --> + <keep>4</keep> + <root>/home/bms/autotesting/tests/</root> + <local>example-live/squeeze/xfce/</local> + <video>autotesting.ogv</video> + <screenshots> + <final>final-screenshot.png</final> + <montage>montage-of-video-frames.png</montage> + </screenshots> + </output> + </test> + </tests> +</autotesting> + + +LOOKING AT EACH TAG +=================== + +<download>http://127.0.0.1/autotesting/debian-live-501-i386-xfce-desktop.iso</download> +The location of the image to be tested. This can be an iso or an USB image (img) or in fact anything that can be booted with qemu. + +<title>Debian Live (xfce)</title> +<description>Debian Live. Daily build of squeeze xfce iso.</description> +The title and description tags are used in the opening titles of the video and also useful when scanning a long tests.xml file. + +<background>http://git.debian.org/?p=debian-live/homepage.git;a=blob_plain;f=images/debian-live.png</background> +The location of the background graphic used in the video. + +<frequency>daily</frequency> +This can be daily, weekly or monthly. Daily, means that the test will be run every time. Weekly - the test will only be done if the current day is a Sunday. Monthly - the test will only be done on the 1st day of a month, + +<xscreen>800x600x24</xscreen> +The width, height and colour depth of the xserver (Xvfb) in which to run qemu. Not at the minimum dimensions required for qemu are 800x600. + +<binary>qemu</binary> +The qemu binary. This could be for example qemu-ppc64 or qemu-arm, to test images for different architectures. + +<options>-cdrom</options> +The option to pass to qemu in conjunction with the image we are testing. In this example we are testing a iso, so we need the "-cdrom" option. For an img file you would use -hda. + +<pause>10</pause> +Time to pause before sending keys presses. + +<sendkeys>kp_enter</sendkeys> +Some iso and img require some user interaction to star the booting, normally just a enter being pressed. Useful for selecting different options from the grub menu. Use the qmeu sendkey command, send "kp_enter" - a keypad enter. To send multiple key presses, use a comma separated list. <sendkeys>down,down,kp_enter</sendkeys>. + +<time>600</time> +Let qemu run for 600 seconds. + +<keep>4</keep> +Number of test sets to keep. If frequency is weekly, and keep is 4, then any test set older than 4 weeks will be deleted. + +<root>/home/bms/autotesting/tests/</root> +<local>example-live/squeeze/xfce/</local> +A new directory will be created at /root/prefix/DATE/, in this example /home/bms/autotesting/tests/example-live/squeeze/xfce/2009-07-01/ and the output files will be stored in this directory. Another directory /root/current/prefix/ will be created, with symlinks to the latest output files, e.g. /home/bms/autotesting/tests/current/example-live/squeeze/xfce/. + +<video>autotesting.ogv</video> +The output video file stored in the output directories (see <root> and <local>). File is a theora video, created by the wonderful application - recordmydeskptop. + +<final>final-screenshot.png</final> +<montage>montage-of-video-frames.png</montage> +The output file names stored in the output directories (see <root> and <local>) of a final screenshot of qemu and a montage of frames from the video. These can give a good idea of how successful the test was rather than viewing the whole video. + + +REQUIRED APPLICATIONS +===================== + +I will be trying to package autotesting - however for reference this following applications are required:- + +python, python-amara, wget, Xvfb, xloadimage, qemu, recordmydesktop, ffmpeg, imagemagick, gmessage + Added: trunk/mmaker/utils/autotesting2/autotesting.py =================================================================== --- trunk/mmaker/utils/autotesting2/autotesting.py (rev 0) +++ trunk/mmaker/utils/autotesting2/autotesting.py 2009-07-01 19:24:57 UTC (rev 2661) @@ -0,0 +1,284 @@ +# -*- coding: utf-8 -*- +# +# autotesting - automatically test by video qemu booting. +# Copyright (C) Brendan M. Sleight, et al. <bm...@ba...> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +from optparse import OptionParser +import amara, datetime, os, shutil, subprocess, telnetlib, tempfile, time + +def displayNumber(): + """Really a global we can change later to something better. + like automaticaly get the next free display number. """ + return ":8" + +def log(message): + """ Print [date] message """ + now = datetime.datetime.now() + print "[" + str(now) + "] " + str(message) + +def cronCheak(frequency): + """Check if the frequency (e.g. daily, weekly, mounthly is due to + be run at this time + returns boolean. + """ + now = datetime.datetime.now() + if frequency == "Daily" or frequency == "daily": + return True + elif frequency == "Weekly" or frequency == "weekly": + if now.weekday() == 0: + return True + elif frequency == "Monthly" or frequency == "monthly": + if now.day == 1: + return True + return False + +def dateBasedOnFrequency(frequency, keep): + now = datetime.datetime.now() + if frequency == "Daily" or frequency == "daily": + days=1 + elif frequency == "Weekly" or frequency == "weekly": + days=7 + elif frequency == "Monthly" or frequency == "monthly": + # No date delta of month so near enough ... + days=30 + old = now - datetime.timedelta(days=days*keep) + return old + +def parseTest(xml): + """ Get the xml file and load as a object usign amara + returns amaraObject""" + tests = amara.parse(xml) + return tests.autotesting.tests + +def wget(url): + """ Use wget to download a file + return fileObject""" + log("Downloading " + url) + tmpFile = tempfile.NamedTemporaryFile(prefix="autotesting_wget_") + L = ['wget', '-nv', str(url), '-O', tmpFile.name] + # Maybe do something with the retcode in the future. + retcode = subprocess.call(L) + return tmpFile + +def getDownloads(downloadURL, backgroundURL): + """ Create two temp files and download the main downlaod and + the background + return tempfile, tempfile""" + background = wget(str(backgroundURL)) + download = wget(str(downloadURL)) + return download, background + +def authorityFile(): + """ Return a file containing "localhost" + """ + authority = tempfile.NamedTemporaryFile(prefix="autotesting_authority_") + authority.write("localhost\n") + authority.close() + return authority + +def startXvfb(display, xscreen, background): + """Start a Xvfb instance. The host display for qemu to run inside. + then set the background image. Return ths PID of Xvfb + return interger""" + log("Starting Xvfb") + authority = authorityFile() + xvfbCommand = ["Xvfb", display, "-auth", authority.name, "-screen", "0", xscreen] + xvfb = subprocess.Popen(xvfbCommand) + log("Setting Xvfb background") + xloadimage = ["xloadimage", "-display", display, "-onroot", "-fullscreen", background] + retcode = subprocess.call(xloadimage) + return xvfb + +def waitUntilEncodingFinished(recordMyDesktop): + log("Waiting for recordmydesktop encoding to finish") + while recordMyDesktop.poll()!=0: + print ".. ", + time.sleep(1) + print " " + +def kill(process, message): + log(message) + kill = ["kill", str(process.pid)] + retcode = subprocess.call(kill) + +def startRecordMyDesktop(display): + log("Starting Record My Desktop") + video = tempfile.NamedTemporaryFile(prefix="autotesting_video_", suffix=".ogv") + recordmydesktopCommand = ["recordmydesktop", "--no-cursor", "-display", + display ,"--no-sound", "--overwrite", "-o" ,video.name] + recordmydesktop = subprocess.Popen(recordmydesktopCommand) + return recordmydesktop, video + +def xmessage(display, title, message, time, font): + xloadimage = ["gmessage", "-display", display, "-timeout", time, + "-font", font, "-button", "", "-title", title, + "-center", "\n" + message] + retcode = subprocess.call(xloadimage) + +def openingTitles(display, test): + log("Showing Opening Titles") + time.sleep(2) + xmessage(display, str(test.title), "Autotesting of: " + str(test.title), "3", "monospace 14") + xmessage(display, str(test.title), str(test.description), "3", "monospace 12") + xmessage(display, str(test.title), "Created at " + str(datetime.datetime.now()), "2", "monospace 10") + +def runningQemu(display, test, qemuDownload): + """ Start qemu running, with a local telnet port at 55555 listening acting as the qemu monitor + returns subprocess.process""" + telnet = ("127.0.0.1", "55555") + address, port = telnet + monitor = "telnet:" + address + ":" + port + ",server,nowait" + qemuBinary = str(test.qemu.binary) + log("Starting " + qemuBinary) + if not qemuBinary.startswith("qemu"): + log("WARNING! : " + qemuBinary + " does not start with qemu, using qemu instead.") + qemuBinary = "qemu" + qemuCommand = [qemuBinary, "-monitor", monitor, "-full-screen", str(test.qemu.options), str(qemuDownload)] + qemu = subprocess.Popen(qemuCommand, env={"DISPLAY": display}) + sendkeysToQemu(test, telnet) + log("Running qemu for " + str(test.qemu.time) + " seconds") + time.sleep(int(str(test.qemu.time))) + return qemu + +def sendkeysToQemu(test, telnet): + """Using telnet to send commands to qemu monitor + """ + address, port = telnet + log("Sending keys after pause of " + str(test.qemu.pause) + " seconds") + time.sleep(int(str(test.qemu.pause))) + tn = telnetlib.Telnet(address, port) + sendkeys = str(test.qemu.sendkeys) + for key in sendkeys.split(','): + log("qemu sendkey " + key) + tn.write("sendkey " + key + "\n") + time.sleep(1) + + +def captureScreenshot(display): + """ Capture screen shot on display + return fileObject""" + log("Capture screenshot") + finalImage = tempfile.NamedTemporaryFile(prefix="autotesting_video_", suffix=".png") + captureCommand = ["import", "-display", display, "-window", "root", finalImage.name] + retcode = subprocess.call(captureCommand) + return finalImage + +def createMontage(video, test): + """ Make 16 frames of the video at 1/16, 2/16, 3/16 .... 16/16 of + way through the video. The make a montage of these frames in to + one image. + return fileObject""" + log("Creating Montage") + montage = tempfile.NamedTemporaryFile(prefix="autotesting_video_", suffix=".png") + videoLength = int(str(test.qemu.pause)) + int(str(test.qemu.time)) + # Time for opening titles + videoLength = videoLength + 2 + 3 + 3 + 2 + listFrames = [] + listFramesNames = [] + for count in range(16): + ss = 0.0625 * count * videoLength + frame = tempfile.NamedTemporaryFile(prefix="autotesting_frame_", suffix=".jpg") + frameName = frame.name + "%d.jpg" + frameNameOut = frame.name + "1.jpg" + ffmpeg = ["ffmpeg", "-i", video.name, "-an", "-ss", str(ss), "-t", + "01", "-r", "1", "-y", frameName] + retcode = subprocess.call(ffmpeg) + shutil.move(frameNameOut, frame.name) + listFramesNames.append(frame.name) + listFrames.append(frame) + montageCommand = ["montage", "-geometry", "180x135+4+4", "-frame", "5"] + for frame in listFramesNames: + montageCommand.append(frame) + montageCommand.append(montage.name) + retcode = subprocess.call(montageCommand) + return montage + +def storeFile(tmpFile, copyLocation, symLocation): + shutil.copyfile(tmpFile, copyLocation) + try: + os.remove(symLocation) + except: + pass + os.symlink(copyLocation, symLocation) + +def fileOutputs(test, video, finalImage, montage): + """ Move files to the correct place (as per XML tags) + including symlinks to current. + """ + log("Moving files to the correct place.") + root=str(test.output.root) + local=str(test.output.local) + today=str(datetime.date.today()) + mainDir = root + "/" + local + "/" + today + "/" + dateDirs = root + "/" + local + "/" + currentDir = root + "/current/" + local + "/" + try: + os.makedirs(mainDir) + except: + pass + try: + os.makedirs(currentDir) + except: + pass + storeFile(video.name, mainDir + str(test.output.video), + currentDir + str(test.output.video)) + storeFile(finalImage.name, mainDir + str(test.output.screenshots.final), + currentDir + str(test.output.screenshots.final)) + storeFile(montage.name, mainDir + str(test.output.screenshots.montage), + currentDir + str(test.output.screenshots.montage)) + keepDate = dateBasedOnFrequency(str(test.frequency), int(str(test.output.keep))) + log("Removing old Autotesting output before " + keepDate.strftime("%A %B %d %I:%M:%S %p %Y")) + for f in os.listdir(dateDirs): + fp = dateDirs + f + if (time.mktime(keepDate.timetuple()) - os.path.getmtime(fp) ) > 0: + # Remove the old file + shutil.rmtree(fp) + log("Removing - " + fp) + +def main(): + usage = "usage: %prog [options] --tests=TESTS.XML \n %prog --help for all options" + parser = OptionParser(usage, version="%prog ") + parser.add_option("-t", "--tests", dest="tests", + help="complete the autotesting definined in the xml template") + (options, args) = parser.parse_args() + if not options.tests : + parser.error("Must pass a list of tests to complete.") + + # Main loop + display = displayNumber() + tests = parseTest(options.tests) + for test in tests.test: + if cronCheak(str(test.frequency)): + log("Starting Autotesting of: " + str(test.title)) + (download, background) = getDownloads(str(test.download), str(test.background)) + xvfb = startXvfb(display, str(test.qemu.xscreen), background.name) + (recordMyDesktop, video) = startRecordMyDesktop(display) + openingTitles(display, test) + qemu = runningQemu(display, test, download.name) + finalImage = captureScreenshot(display) + kill(qemu, "Killing qemu") + kill(recordMyDesktop, "Killing recordmysdesktop") + waitUntilEncodingFinished(recordMyDesktop) + kill(xvfb, "Killing Xvfb") + # Put video, montage in right place remvoe old versions etc. + montage = createMontage(video, test) + fileOutputs(test, video, finalImage, montage) + log("Finished Autotesting of: " + str(test.title)) + log("*****************************") + +if __name__ == "__main__": + main() Added: trunk/mmaker/utils/autotesting2/tests/local-test_morphix-lightgui.xml =================================================================== --- trunk/mmaker/utils/autotesting2/tests/local-test_morphix-lightgui.xml (rev 0) +++ trunk/mmaker/utils/autotesting2/tests/local-test_morphix-lightgui.xml 2009-07-01 19:24:57 UTC (rev 2661) @@ -0,0 +1,32 @@ +<autotesting> + <tests> + <test> + <download>http://127.0.0.1/autotesting/lightgui-basemod-2.6.23-2008-02-10_0018-latest.iso</download> + <title>Morphix LightGUI</title> + <description>Morphix. Daily build of LightGUI iso.</description> + <background>http://www.morphix.org/templates/MorphixORG/images/mambo_header.jpg</background> + <frequency>daily</frequency> + <qemu> + <xscreen>800x600x24</xscreen> + <binary>qemu</binary> + <options>-cdrom</options> + <!-- Time to pause before sending keys presses --> + <pause>10</pause> + <sendkeys>down,down,kp_enter</sendkeys> + <!-- Time to run qmeu --> + <time>600</time> + </qemu> + <output> + <!-- Number of test sets to keep --> + <keep>4</keep> + <root>/home/bms/autotesting/tests/</root> + <local>morphix/lightGUI/</local> + <video>autotesting.ogv</video> + <screenshots> + <final>final-screenshot.png</final> + <montage>montage-of-video-frames.png</montage> + </screenshots> + </output> + </test> + </tests> +</autotesting> Added: trunk/mmaker/utils/autotesting2/tests/morphix-lightgui.xml =================================================================== --- trunk/mmaker/utils/autotesting2/tests/morphix-lightgui.xml (rev 0) +++ trunk/mmaker/utils/autotesting2/tests/morphix-lightgui.xml 2009-07-01 19:24:57 UTC (rev 2661) @@ -0,0 +1,32 @@ +<autotesting> + <tests> + <test> + <download>http://distro.ibiblio.org/pub/linux/distributions/morphix/autobuilds/iso/lightgui-basemod-2.6.23-2008-02-10_0018-latest.iso</download> + <title>Morphix LightGUI</title> + <description>Morphix. Daily build of LightGUI iso.</description> + <background>http://www.morphix.org/templates/MorphixORG/images/mambo_header.jpg</background> + <frequency>daily</frequency> + <qemu> + <xscreen>800x600x24</xscreen> + <binary>qemu</binary> + <options>-cdrom</options> + <!-- Time to pause before sending keys presses --> + <pause>10</pause> + <sendkeys>down,down,kp_enter</sendkeys> + <!-- Time to run qmeu --> + <time>600</time> + </qemu> + <output> + <!-- Number of test sets to keep --> + <keep>4</keep> + <root>/home/bms/autotesting/tests/</root> + <local>morphix/lightGUI/</local> + <video>autotesting.ogv</video> + <screenshots> + <final>final-screenshot.png</final> + <montage>montage-of-video-frames.png</montage> + </screenshots> + </output> + </test> + </tests> +</autotesting> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |