xmpedit

GTK+ editor for XMP metadata embedded in images
git clone https://code.djc.id.au/git/xmpedit/
commit 91a96bc2989ab7539ca75caff9440446f71075a7
parent 471b8ff7024ec33f17cc4486b514d945e3248eb8
Author: Dan Callaghan <djc@djc.id.au>
Date:   Sat, 18 Sep 2010 23:11:35 +1000

Python GUI tests using dogtail and python-xlib

Diffstat:
Mbuild | 4++++
Atest/guitest.py | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atest/printxmp.cpp | 19+++++++++++++++++++
3 files changed, 103 insertions(+), 0 deletions(-)
diff --git a/build b/build
@@ -59,11 +59,15 @@ class Builder(object):
         self.link(target='xmpedit')
     
     def test(self):
+        # unit tests
         self.compile_vala(defs=['DEBUG', 'TEST'])
         self.compile_c()
         self.compile_cpp()
         self.link(target='xmpedit_test')
         invoke(['gtester', '--verbose', 'target/xmpedit_test'])
+        # gui tests
+        invoke(['g++', '-lexiv2', '-o', 'target/printxmp', 'test/printxmp.cpp']) # XXX
+        invoke(['test/guitest.py'])
 
     def compile_vala(self, defs=[]):
         invoke(['valac', '-C', '-d', 'target/valac'] +
diff --git a/test/guitest.py b/test/guitest.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+
+import sys
+import os
+import time
+import struct
+import tempfile
+import subprocess
+import Xlib.display
+import dogtail.tree
+import unittest
+
+def extract_xmp(path):
+    popen = subprocess.Popen([
+            os.path.join('target', 'printxmp'), path],
+            stdout=subprocess.PIPE)
+    stdout, stderr = popen.communicate()
+    assert popen.returncode == 0
+    return stdout.decode('utf8')
+
+class XmpeditTestCase(unittest.TestCase):
+
+    def start(self, image_filename):
+        self.tempfile = tempfile.NamedTemporaryFile()
+        self.tempfile.write(open(os.path.join('testdata', image_filename)).read())
+        self.tempfile.flush()
+        self.popen = subprocess.Popen(
+                [os.path.join('target', 'xmpedit'), self.tempfile.name])
+
+    def stop(self):
+        if self.popen.returncode is None:
+            print 'Terminating process under test'
+            self.popen.terminate()
+            self.popen.wait()
+
+    def assert_stopped(self):
+        for _ in range(3):
+            if self.popen.poll() is not None:
+                assert self.popen.returncode == 0
+                return
+            time.sleep(1)
+        assert False, 'Process did not end'
+
+    def get_window(self):
+        root = Xlib.display.Display().screen().root # XXX multiple screens?
+        for child in root.query_tree().children:
+            if child.get_wm_class() is not None and 'xmpedit' in child.get_wm_class():
+                return child
+    
+    def close_window(self):
+        window = self.get_window()
+        WM_PROTOCOLS = window.display.get_atom('WM_PROTOCOLS')
+        WM_DELETE_WINDOW = window.display.get_atom('WM_DELETE_WINDOW')
+        assert WM_DELETE_WINDOW in window.get_wm_protocols()
+        event = Xlib.protocol.event.ClientMessage(window=window, client_type=WM_PROTOCOLS,
+                data=(32, struct.pack('=lllll', WM_DELETE_WINDOW, 0, 0, 0, 0)))
+        window.send_event(event)
+        window.display.flush()
+    
+class Test(XmpeditTestCase):
+
+    def setUp(self):
+        self.start('24-06-06_1449.jpg')
+
+    def tearDown(self):
+        self.stop()
+        
+    def test_roundtrip(self):
+        xmpedit = dogtail.tree.Root().application('xmpedit')
+        window = xmpedit.child(roleName='frame')
+        time.sleep(0.5)
+        self.close_window()
+        self.assert_stopped()
+        xmp = extract_xmp(self.tempfile.name)
+        self.assertEquals(len(xmp), 2675) # 
+        self.assertEquals(extract_xmp(self.tempfile.name),
+                u'''<?xpacket begin="\ufeff" id="W5M0MpCehiHzreSzNTczkc9d"?><x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="xmpedit 0.0-dev"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about=""><Iptc4xmlCore:Location xmlns:Iptc4xmlCore="http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/">UQ St Lucia</Iptc4xmlCore:Location><dc:description xmlns:dc="http://purl.org/dc/elements/1.1/"><rdf:Alt><rdf:li xml:lang="x-default">Edward Scissorhands stencil graffiti on the wall of John Hines building.</rdf:li></rdf:Alt></dc:description></rdf:Description></rdf:RDF></x:xmpmeta>''' + ' ' * 2079 + '''<?xpacket end="w"?>''')
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/test/printxmp.cpp b/test/printxmp.cpp
@@ -0,0 +1,19 @@
+/*
+ * xmpedit
+ * Copyright 2010 Dan Callaghan <djc@djc.id.au>
+ * Released under GPLv2
+ */
+
+#include <iostream>
+#include <exiv2/image.hpp>
+
+/** Simplest possible utility to extract raw XMP packet from a file. */
+int main(int argc, char *argv[]) {
+    if (argc != 2)
+        return 1;
+    const std::string path(argv[1]);
+    std::auto_ptr<Exiv2::Image> image(Exiv2::ImageFactory::open(path));
+    image->readMetadata();
+    std::cout << image->xmpPacket();
+    return 0;
+}