[tracker/rss-enclosures] Adding common infrastructure for the new tests



commit 29187f500ccd09f8bd750a9be08acc7bbaad89db
Author: Ivan Frade <ivan frade nokia com>
Date:   Wed Aug 18 18:20:43 2010 +0300

    Adding common infrastructure for the new tests

 tests/functional-tests/common/Makefile.am          |    5 +
 .../common/data/Doc/performance.doc                |  Bin 0 -> 82944 bytes
 .../common/data/Images/Makefile.am                 |   10 +
 .../common/data/Images/test-image-1.jpg            |  Bin 0 -> 7558 bytes
 .../common/data/Images/test-image-2.png            |  Bin 0 -> 2374 bytes
 .../common/data/Images/test-image-3.tif            |  Bin 0 -> 4594 bytes
 tests/functional-tests/common/data/Makefile.am     |   15 +
 .../functional-tests/common/data/Music/Makefile.am |    8 +
 .../common/data/Music/tracker-mp3-test.mp3         |  Bin 0 -> 61547 bytes
 .../common/data/Pdf/office-tools-test-document.pdf |  Bin 0 -> 236639 bytes
 tests/functional-tests/common/data/Ppt/al-cont.ppt |  Bin 0 -> 1508864 bytes
 .../functional-tests/common/data/Video/Makefile.am |   10 +
 .../common/data/Video/test-video.mp4               |  Bin 0 -> 211931 bytes
 tests/functional-tests/common/data/pickled_Images  |  Bin 0 -> 1203 bytes
 tests/functional-tests/common/data/pickled_Music   |  Bin 0 -> 877 bytes
 tests/functional-tests/common/utils/.gitignore     |    1 +
 tests/functional-tests/common/utils/Makefile.am    |   16 +
 .../common/utils/configuration.py.in               |   75 +++++
 .../common/utils/expectedFailure.py                |   48 +++
 tests/functional-tests/common/utils/helpers.py     |  281 +++++++++++++++++
 tests/functional-tests/common/utils/html.py        |   64 ++++
 tests/functional-tests/common/utils/minertest.py   |   99 ++++++
 tests/functional-tests/common/utils/storetest.py   |   47 +++
 tests/functional-tests/common/utils/system.py      |  329 ++++++++++++++++++++
 24 files changed, 1008 insertions(+), 0 deletions(-)
---
diff --git a/tests/functional-tests/common/Makefile.am b/tests/functional-tests/common/Makefile.am
new file mode 100644
index 0000000..a29a93c
--- /dev/null
+++ b/tests/functional-tests/common/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = utils
+
+commondir = $(datadir)/tracker-tests/common
+
+common_SCRIPTS = __init__.py
\ No newline at end of file
diff --git a/tests/functional-tests/common/__init__.py b/tests/functional-tests/common/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/functional-tests/common/data/Doc/performance.doc b/tests/functional-tests/common/data/Doc/performance.doc
new file mode 100644
index 0000000..8450cdd
Binary files /dev/null and b/tests/functional-tests/common/data/Doc/performance.doc differ
diff --git a/tests/functional-tests/common/data/Images/Makefile.am b/tests/functional-tests/common/data/Images/Makefile.am
new file mode 100644
index 0000000..69d089e
--- /dev/null
+++ b/tests/functional-tests/common/data/Images/Makefile.am
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Makefile.decl
+
+configdir = $(datadir)/tracker-tests/data/Images
+
+config_DATA =			\
+	test-image-1.jpg	\
+	test-image-2.png	\
+	test-image-3.tif
+
+EXTRA_DIST = $(config_DATA)
diff --git a/tests/functional-tests/common/data/Images/test-image-1.jpg b/tests/functional-tests/common/data/Images/test-image-1.jpg
new file mode 100644
index 0000000..f1f917b
Binary files /dev/null and b/tests/functional-tests/common/data/Images/test-image-1.jpg differ
diff --git a/tests/functional-tests/common/data/Images/test-image-2.png b/tests/functional-tests/common/data/Images/test-image-2.png
new file mode 100644
index 0000000..7ff9788
Binary files /dev/null and b/tests/functional-tests/common/data/Images/test-image-2.png differ
diff --git a/tests/functional-tests/common/data/Images/test-image-3.tif b/tests/functional-tests/common/data/Images/test-image-3.tif
new file mode 100644
index 0000000..8d91556
Binary files /dev/null and b/tests/functional-tests/common/data/Images/test-image-3.tif differ
diff --git a/tests/functional-tests/common/data/Makefile.am b/tests/functional-tests/common/data/Makefile.am
new file mode 100644
index 0000000..231f058
--- /dev/null
+++ b/tests/functional-tests/common/data/Makefile.am
@@ -0,0 +1,15 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS = 		\
+	Music		\
+	Video		\
+	Images		\
+	Text		
+
+configdir = $(datadir)/tracker-tests/data
+
+config_DATA =			\
+	pickled_Music		\
+	pickled_Images
+
+EXTRA_DIST = $(config_DATA)
diff --git a/tests/functional-tests/common/data/Music/Makefile.am b/tests/functional-tests/common/data/Music/Makefile.am
new file mode 100644
index 0000000..0166f9f
--- /dev/null
+++ b/tests/functional-tests/common/data/Music/Makefile.am
@@ -0,0 +1,8 @@
+include $(top_srcdir)/Makefile.decl
+
+configdir = $(datadir)/tracker-tests/data/Music
+
+config_DATA =			\
+	tracker-mp3-test.mp3
+
+EXTRA_DIST = $(config_DATA)
diff --git a/tests/functional-tests/common/data/Music/tracker-mp3-test.mp3 b/tests/functional-tests/common/data/Music/tracker-mp3-test.mp3
new file mode 100644
index 0000000..bbd4c76
Binary files /dev/null and b/tests/functional-tests/common/data/Music/tracker-mp3-test.mp3 differ
diff --git a/tests/functional-tests/common/data/Pdf/office-tools-test-document.pdf b/tests/functional-tests/common/data/Pdf/office-tools-test-document.pdf
new file mode 100644
index 0000000..064645c
Binary files /dev/null and b/tests/functional-tests/common/data/Pdf/office-tools-test-document.pdf differ
diff --git a/tests/functional-tests/common/data/Ppt/al-cont.ppt b/tests/functional-tests/common/data/Ppt/al-cont.ppt
new file mode 100644
index 0000000..2fc0a73
Binary files /dev/null and b/tests/functional-tests/common/data/Ppt/al-cont.ppt differ
diff --git a/tests/functional-tests/common/data/Video/Makefile.am b/tests/functional-tests/common/data/Video/Makefile.am
new file mode 100644
index 0000000..fafae7d
--- /dev/null
+++ b/tests/functional-tests/common/data/Video/Makefile.am
@@ -0,0 +1,10 @@
+include $(top_srcdir)/Makefile.decl
+
+if DIST_FUNCTIONAL_TESTS
+configdir = $(datadir)/tracker-tests/data/Video
+
+config_DATA =			\
+	test-video.mp4
+
+EXTRA_DIST = $(config_DATA)
+endif
diff --git a/tests/functional-tests/common/data/Video/test-video.mp4 b/tests/functional-tests/common/data/Video/test-video.mp4
new file mode 100644
index 0000000..915e4be
Binary files /dev/null and b/tests/functional-tests/common/data/Video/test-video.mp4 differ
diff --git a/tests/functional-tests/common/data/pickled_Images b/tests/functional-tests/common/data/pickled_Images
new file mode 100644
index 0000000..330cf46
Binary files /dev/null and b/tests/functional-tests/common/data/pickled_Images differ
diff --git a/tests/functional-tests/common/data/pickled_Music b/tests/functional-tests/common/data/pickled_Music
new file mode 100644
index 0000000..1913fc4
Binary files /dev/null and b/tests/functional-tests/common/data/pickled_Music differ
diff --git a/tests/functional-tests/common/utils/.gitignore b/tests/functional-tests/common/utils/.gitignore
new file mode 100644
index 0000000..7c0b746
--- /dev/null
+++ b/tests/functional-tests/common/utils/.gitignore
@@ -0,0 +1 @@
+configuration.py
diff --git a/tests/functional-tests/common/utils/Makefile.am b/tests/functional-tests/common/utils/Makefile.am
new file mode 100644
index 0000000..8223d2f
--- /dev/null
+++ b/tests/functional-tests/common/utils/Makefile.am
@@ -0,0 +1,16 @@
+include $(top_srcdir)/Makefile.decl
+
+utilsdir = $(datadir)/tracker-tests/common/utils
+
+utils_SCRIPTS = 		\
+	__init__.py		\
+	configuration.py	\
+	expectedFailure.py	\
+	helpers.py		\
+	storetest.py		\
+	minertest.py		\
+	system.py
+
+EXTRA_DIST = 			\
+	configuration.py.in	\
+	$(utils_SCRIPTS)
diff --git a/tests/functional-tests/common/utils/__init__.py b/tests/functional-tests/common/utils/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/functional-tests/common/utils/configuration.py.in b/tests/functional-tests/common/utils/configuration.py.in
new file mode 100644
index 0000000..d745dd4
--- /dev/null
+++ b/tests/functional-tests/common/utils/configuration.py.in
@@ -0,0 +1,75 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2010, Nokia <jean-luc lamadon nokia com>
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+
+"Constants describing Tracker D-Bus services"
+
+import os
+
+TRACKER_BUSNAME = 'org.freedesktop.Tracker1'
+TRACKER_OBJ_PATH = '/org/freedesktop/Tracker1/Resources'
+RESOURCES_IFACE = "org.freedesktop.Tracker1.Resources"
+
+MINERFS_BUSNAME = "org.freedesktop.Tracker1.Miner.Files"
+MINERFS_OBJ_PATH = "/org/freedesktop/Tracker1/Miner/Files"
+MINER_IFACE = "org.freedesktop.Tracker1.Miner"
+
+TRACKER_BACKUP_OBJ_PATH = "/org/freedesktop/Tracker1/Backup"                                            
+BACKUP_IFACE = "org.freedesktop.Tracker1.Backup"
+
+TRACKER_EXTRACT_BUSNAME = "org.freedesktop.Tracker1.Extract"
+TRACKER_EXTRACT_OBJ_PATH = "/org/freedesktop/Tracker1/Extract"
+TRACKER_EXTRACT_IFACE = "org.freedesktop.Tracker1.Extract"
+
+WRITEBACK_BUSNAME = "org.freedesktop.Tracker1.Writeback"
+
+def expandvars (variable):
+    # Note: the order matters!
+    result = variable
+    for var, value in [("${datarootdir}", RAW_DATAROOT_DIR),
+                       ("${exec_prefix}", RAW_EXEC_PREFIX),
+                       ("${prefix}", PREFIX)]:
+        result = result.replace (var, value)
+
+
+    return result
+
+
+
+PREFIX = "@prefix@"
+#
+# This raw variables are set by autotools without translating vars:
+#   E.G. bindir='${exec_prefix}/bin
+#
+# So we do the translation by hand in the expandvars function
+#
+RAW_EXEC_PREFIX = "@exec_prefix@"
+RAW_EXEC_DIR = "@libexecdir@"
+RAW_DATA_DIR = "@datadir@"
+RAW_DATAROOT_DIR = "@datarootdir@"
+RAW_BINDIR = "@bindir@"
+
+EXEC_PREFIX = os.path.normpath (expandvars (RAW_EXEC_DIR))
+DATADIR = os.path.normpath (expandvars (RAW_DATA_DIR))
+BINDIR = os.path.normpath (expandvars (RAW_BINDIR))
+                            
+haveMaemo = ("@HAVE_MAEMO_TRUE@" == "")
+
+TEST_TMP_DIR = os.path.join (os.environ["HOME"], ".tracker-tests")
+        
diff --git a/tests/functional-tests/common/utils/expectedFailure.py b/tests/functional-tests/common/utils/expectedFailure.py
new file mode 100644
index 0000000..f8ced47
--- /dev/null
+++ b/tests/functional-tests/common/utils/expectedFailure.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+## Code taken and modified from unittest2 framework (case.py)
+
+## Copyright (c) 1999-2003 Steve Purcell
+## Copyright (c) 2003-2010 Python Software Foundation
+## Copyright (c) 2010, Nokia (ivan frade nokia com)
+
+## This module is free software, and you may redistribute it and/or modify
+## it under the same terms as Python itself, so long as this copyright message
+## and disclaimer are retained in their original form.
+
+## IN NO EVENT SHALL THE AUTHOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
+## SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
+## THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+## DAMAGE.
+
+## THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
+## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+## PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS,
+## AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
+## SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+
+"""
+Write values in tracker and check the actual values are written
+on the files. Note that these tests are highly platform dependant.
+"""
+import sys
+import unittest2 as ut
+from unittest2.compatibility import wraps
+
+def expectedFailureBug(bugnumber):
+    """
+    Decorator to mark bugs with ExpectedFailure. In case that a expected failure PASS
+    it will raise an exception pointing to the Bug number.
+
+    Keep your bugs and tests in sync!
+    """
+    def decorator (func):
+        @wraps(func)
+        def wrapper(*args, **kwargs):
+            try:
+                func(*args, **kwargs)
+            except Exception:
+                raise ut.case._ExpectedFailure(sys.exc_info())
+            raise Exception ("Unexpected success. This should fail because of bug " +str(bugnumber))
+        return wrapper
+    return decorator
diff --git a/tests/functional-tests/common/utils/helpers.py b/tests/functional-tests/common/utils/helpers.py
new file mode 100644
index 0000000..b3b78be
--- /dev/null
+++ b/tests/functional-tests/common/utils/helpers.py
@@ -0,0 +1,281 @@
+#!/usr/bin/python
+#
+# Copyright (C) 2010, Nokia <jean-luc lamadon nokia com>
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+import dbus
+import gobject
+import commands
+import os
+import signal
+from dbus.mainloop.glib import DBusGMainLoop
+import configuration as cfg
+import re
+
+class StoreHelper:
+    """
+    Wrapper for the Store API
+
+    Every method tries to reconnect once if there is a dbus exception
+    (some tests kill the daemon and make the connection useless)
+    """
+
+    def __init__ (self):
+        self.connect ()
+        
+    def connect (self):
+        dbus_loop = DBusGMainLoop(set_as_default=True)
+        bus = dbus.SessionBus (mainloop=dbus_loop)
+        tracker = bus.get_object (cfg.TRACKER_BUSNAME, cfg.TRACKER_OBJ_PATH)
+        self.resources = dbus.Interface (tracker,
+                                         dbus_interface=cfg.RESOURCES_IFACE)
+        tracker_backup = bus.get_object (cfg.TRACKER_BUSNAME, cfg.TRACKER_BACKUP_OBJ_PATH)
+        self.backup_iface = dbus.Interface (tracker_backup, dbus_interface=cfg.BACKUP_IFACE)
+
+    def query (self, query, timeout=5000):
+        try:
+            return self.resources.SparqlQuery (query, timeout=timeout)
+        except dbus.DBusException:
+            self.connect ()
+            return self.resources.SparqlQuery (query, timeout=timeout)
+
+    def update (self, update_sparql, timeout=5000):
+        try:
+            return self.resources.SparqlUpdate (update_sparql, timeout=timeout)
+        except dbus.DBusException:
+            self.connect ()
+            return self.resources.SparqlUpdate (update_sparql, timeout=timeout)
+
+    def batch_update (self, update_sparql):
+        try:
+            return self.resources.BatchSparqlUpdate (update_sparql)
+        except dbus.DBusException:
+            self.connect ()
+            return self.resources.BatchSparqlUpdate (update_sparql)
+
+    def batch_commit (self):
+        return self.resources.BatchCommit ()
+
+    def backup (self, backup_file):
+        try:
+            self.backup_iface.Save (backup_file)
+        except dbus.DBusException:
+            self.connect ()
+            self.backup_iface.Save (backup_file)
+            
+    def restore (self, backup_file):
+        try:
+            return self.backup_iface.Restore (backup_file)
+        except dbus.DBusException:
+            self.connect ()
+            return self.backup_iface.Restore (backup_file)
+
+    def get_tracker_iface (self):
+        return self.resources
+
+    def count_instances (self, ontology_class):
+        QUERY = """
+        SELECT COUNT(?u) WHERE {
+            ?u a %s .
+        }
+        """
+        try:
+            result = self.resources.SparqlQuery (QUERY % (ontology_class))
+        except dbus.DBusException:
+            self.connect ()
+            result = self.resources.SparqlQuery (QUERY % (ontology_class))
+            
+        if (len (result) == 1):
+            return int (result [0][0])
+        else:
+            return -1
+
+
+class MinerHelper ():
+
+    def __init__ (self):
+        self.connect ()
+
+    def connect (self):
+        dbus_loop = DBusGMainLoop(set_as_default=True)
+        bus = dbus.SessionBus (mainloop=dbus_loop)
+        minerfs = bus.get_object (cfg.MINERFS_BUSNAME, cfg.MINERFS_OBJ_PATH)
+        self.miner_fs = dbus.Interface (minerfs,
+                                        dbus_interface=cfg.MINER_IFACE)
+        
+    def ignore (self, filelist):
+        self.miner_fs.IgnoreNextUpdate (filelist)
+
+        
+class ExtractorHelper ():
+
+    def __init__ (self):
+        self.connect ()
+    
+    def connect (self):
+        dbus_loop = DBusGMainLoop(set_as_default=True)
+        bus = dbus.SessionBus (mainloop=dbus_loop)
+        tracker = bus.get_object (cfg.TRACKER_EXTRACT_BUSNAME, cfg.TRACKER_EXTRACT_OBJ_PATH)
+        self.extractor = dbus.Interface (tracker,
+                                         dbus_interface=cfg.TRACKER_EXTRACT_IFACE)
+
+
+    def get_metadata (self, filename, mime):
+        """
+        Calls the extractor a returns a dictionary of property, value.
+        Example:
+         { 'nie:filename': 'a.jpeg' ,
+           'tracker:added': '2008-12-12T12:23:34Z'
+         }
+        """
+        metadata = {}
+        preupdate, embedded = self.extractor.GetMetadata (filename, mime)
+        for attribute_value in self.__process_lines (embedded):
+            att, value = attribute_value.split (" ", 1)
+            if metadata.has_key (att):
+                metadata [att].append (value)
+            else:
+                metadata [att] = [value]
+
+        return metadata
+            
+    def __process_lines (self, embedded):
+        """
+        Would be so cool to implement this with yield and generators... :)
+        """
+        grouped_lines = []
+        current_line = ""
+        anon_node_open = False
+        for l in embedded.split ("\n\t"):
+            if "[" in l:
+                current_line = current_line + l
+                anon_node_open = True
+                continue
+
+            if "]" in l:
+                anon_node_open = False
+                current_line += l
+                final_lines = self.__handle_anon_nodes (current_line.strip ())
+                grouped_lines = grouped_lines + final_lines
+                current_line = ""
+                continue
+
+            if anon_node_open:
+                current_line += l
+            else:
+                if (len (l.strip ()) == 0):
+                    continue
+                    
+                final_lines = self.__handle_multivalues (l.strip ())
+                grouped_lines = grouped_lines + final_lines
+
+        return map (self.__clean_value, grouped_lines)
+
+    def __handle_multivalues (self, line):
+        """
+        Split multivalues like:
+        a nfo:Image, nmm:Photo ;
+           -> a nfo:Image ;
+           -> a nmm:Photo ;
+        """
+        hasEscapedComma = re.compile ("\".+,.+\"")
+
+        if "," in line and not hasEscapedComma.search (line):
+            prop, multival = line.split (" ", 1)
+            results = []
+            for value in multival.split (","):
+                results.append ("%s %s" % (prop, value.strip ()))
+            return results
+        else:
+            return [line]
+        
+
+        
+            
+    def __handle_anon_nodes (self, line):
+        """
+        Traslates anonymous nodes in 'flat' properties:
+
+        nao:hasTag [a nao:Tag; nao:prefLabel "xxx"]
+                 -> nao:hasTag:prefLabel "xxx"
+                 
+        mlo:location [a mlo:GeoPoint; mlo:city "Delhi"; mlo:country "India"]
+                -> mlo:location:city "Delhi"
+                -> mlo:location:country "India"
+        """
+        
+        # hasTag case
+        if line.startswith ("nao:hasTag"):
+            getlabel = re.compile ("nao:prefLabel\ \"(\w+)\"")
+            match = getlabel.search (line)
+            if (match):
+                line = 'nao:hasTag:prefLabel "%s" ;' % (match.group(1))
+                return [line]
+            else:
+                print "Whats wrong on line", line, "?"
+                return [line]
+
+        # location case
+        elif line.startswith ("mlo:location"):
+
+            results = []
+
+            # Can have country AND/OR city
+            getcountry = re.compile ("mlo:country\ \"(\w+)\"")
+            getcity = re.compile ("mlo:city\ \"(\w+)\"")
+            
+            country_match = getcountry.search (line)
+            city_match = getcity.search (line)
+            
+            if (country_match):
+                results.append ('mlo:location:country "%s" ;' % (country_match.group(1)))
+
+            if (city_match):
+                results.append ('mlo:location:city "%s" ;' % (city_match.group(1)))
+
+            if (not country_match and not city_match):
+                print "FIXME another location subproperty in ", line
+
+            return results
+        elif line.startswith ("nco:creator"):
+            getcreator = re.compile ("nco:fullname\ \"(\w+)\"")
+            creator_match = getcreator.search (line)
+
+            if (creator_match):
+                new_line = 'nco:creator:fullname "%s" ;' % (creator_match.group (1))
+                return [new_line]
+            else:
+                print "Something special in this line '%s'" % (line)
+
+        else:
+            return [line]
+
+    def __clean_value (self, value):
+        """
+        the value comes with a ';' or a '.' at the end
+        """
+        if (len (value) < 2):
+            return value.strip ()
+        
+        clean = value.strip ()
+        if value[-1] in [';', '.']:
+            clean = value [:-1]
+
+        clean = clean.replace ("\"", "")
+            
+        return clean.strip ()
+        
diff --git a/tests/functional-tests/common/utils/html.py b/tests/functional-tests/common/utils/html.py
new file mode 100644
index 0000000..0acf513
--- /dev/null
+++ b/tests/functional-tests/common/utils/html.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python2.5
+import unittest
+import os
+
+class html:
+	
+	def top(self):
+
+	        os.remove('indexing-performance')	
+		self.file = 'indexing-performance' 
+		self.f = open(self.file, "a")
+		self.f.write('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd";>' + "\n" +
+		'<html>' + "\n" +
+		' <head>' + "\n" +
+		' <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">' + "\n" +
+		' <title>Tracker Indexing Performance</title>' + "\n" +
+		' <H1 align="center"><font color="#CC0000" face="verdana" size="6">Tracker Indexing Performance</font></H1>' + "\n" +
+		' <body>' + "\n" +
+		' <table border="1", align="center">' + "\n" + 
+		'<th><font color="#8000FF" face="verdana" size="4">Test data</font></th>' + "\n" +
+		'<th><font color="#8000FF" face="verdana" size="4">Minimum</font></th>' + "\n" +
+		'<th><font color="#8000FF" face="verdana" size="4">Maximum</font></th>' + "\n" +
+		'<th><font color="#8000FF" face="verdana" size="4">Average</font></th>' + "\n" +
+		'<th><font color="#8000FF" face="verdana" size="4">Median</font></th>' + "\n" 
+		)
+		self.f.close() 
+		
+	
+	def mid(self,title,min,max,avg,median):
+
+		self.file = 'indexing-performance' 
+		self.f = open(self.file, "a")
+		self.f.write( '<tr>' + "\n" +
+		'<td>' + title + '</td>' + "\n" +
+		'<td>' + str(min) + '</td>' + "\n" +
+		'<td>' + str(max) + '</td>' + "\n" +
+		'<td>' + str(avg) + '</td>' + "\n" +
+		'<td>' + str(median) + '</td>' + "\n" +
+		'</tr>' + "\n" 
+		)
+		self.f.close() 
+
+	def bottom(self):
+
+		self.file = 'indexing-performance' 
+		self.f = open(self.file, "a")
+		self.f.write( '</table>' + "\n" +
+		' </body>' + "\n" +
+		' </head>' + "\n" +
+		' </html>' + "\n" 
+		)
+		self.f.close() 
+
+class report(unittest.TestCase):
+
+        def first(self):                      
+                self.file = html()
+                self.file.top()                                    
+                                                             
+        def last(self):                                        
+                self.file = html()
+                self.file.bottom()
+
+
diff --git a/tests/functional-tests/common/utils/minertest.py b/tests/functional-tests/common/utils/minertest.py
new file mode 100644
index 0000000..77ff45f
--- /dev/null
+++ b/tests/functional-tests/common/utils/minertest.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2010, Nokia <ivan frade nokia com>
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+from common.utils import configuration as cfg
+from common.utils.system import TrackerSystemAbstraction
+from common.utils.helpers import StoreHelper
+import unittest2 as ut
+
+import shutil
+import os
+
+BASEDIR = os.environ['HOME']
+
+def path (filename):
+    return os.path.join (BASEDIR, filename)
+
+def uri (filename):
+    return "file://" + os.path.join (BASEDIR, filename)
+
+
+DEFAULT_TEXT = "Some stupid content, to have a test file"
+
+class CommonTrackerMinerTest (ut.TestCase):
+
+    @classmethod
+    def __prepare_directories (self):
+        #
+        #     ~/test-monitored/
+        #                     /file1.txt
+        #                     /dir1/
+        #                          /file2.txt
+        #                          /dir2/
+        #                               /file3.txt
+        #
+        #
+        #     ~/test-no-monitored/
+        #                        /file0.txt
+        #
+        
+        for d in ["test-monitored",
+                  "test-monitored/dir1",
+                  "test-monitored/dir1/dir2",
+                  "test-no-monitored"]:
+            directory = os.path.join (BASEDIR, d)
+            if (os.path.exists (directory)):
+                shutil.rmtree (directory)
+            os.makedirs (directory)
+
+        for tf in ["test-monitored/file1.txt",
+                   "test-monitored/dir1/file2.txt",
+                   "test-monitored/dir1/dir2/file3.txt",
+                   "test-no-monitored/file0.txt"]:
+            testfile = os.path.join (BASEDIR, tf)
+            if (os.path.exists (testfile)):
+                os.remove (testfile)
+            f = open (testfile, 'w')
+            f.write (DEFAULT_TEXT)
+            f.close ()
+
+    
+    @classmethod 
+    def setUpClass (self):
+        #print "Starting the daemon in test mode"
+        self.__prepare_directories ()
+        
+        self.system = TrackerSystemAbstraction ()
+
+        if (os.path.exists (os.getcwd() + "/test-configurations/miner-basic-ops")):
+            # Use local directory if available
+            confdir = os.getcwd() + "/test-configurations/miner-basic-ops"
+        else:
+            confdir = os.path.join (cfg.DATADIR, "tracker-tests",
+                                    "test-configurations", "miner-basic-ops")
+        self.system.tracker_miner_fs_testing_start (confdir)
+        # Returns when ready
+        self.tracker = StoreHelper ()
+        print "Ready to go!"
+        
+    @classmethod
+    def tearDownClass (self):
+        #print "Stopping the daemon in test mode (Doing nothing now)"
+        self.system.tracker_miner_fs_testing_stop ()
+
diff --git a/tests/functional-tests/common/utils/storetest.py b/tests/functional-tests/common/utils/storetest.py
new file mode 100644
index 0000000..47af43e
--- /dev/null
+++ b/tests/functional-tests/common/utils/storetest.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2010, Nokia <ivan frade nokia com>
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+import time
+
+#sys.path.insert (0, "../..")
+
+from common.utils.system import TrackerSystemAbstraction
+from common.utils.helpers import StoreHelper
+from common.utils import configuration as cfg
+
+import unittest2 as ut
+#import unittest as ut
+
+class CommonTrackerStoreTest (ut.TestCase):
+        """
+        Common superclass for tests that just require a fresh store running
+        """
+        @classmethod 
+	def setUpClass (self):
+            #print "Starting the daemon in test mode"
+            self.system = TrackerSystemAbstraction ()
+            self.system.tracker_store_testing_start ()
+            time.sleep (1)
+            self.tracker = StoreHelper ()
+
+        @classmethod
+        def tearDownClass (self):
+            #print "Stopping the daemon in test mode (Doing nothing now)"
+            self.system.tracker_store_testing_stop ()
+            time.sleep (2)
diff --git a/tests/functional-tests/common/utils/system.py b/tests/functional-tests/common/utils/system.py
new file mode 100644
index 0000000..8cb4286
--- /dev/null
+++ b/tests/functional-tests/common/utils/system.py
@@ -0,0 +1,329 @@
+import os
+import subprocess
+import shutil
+import configuration as cfg
+
+import gobject
+import glib
+import dbus
+from dbus.mainloop.glib import DBusGMainLoop
+import time
+
+# Don't use /tmp (not enough space there)
+
+TEST_ENV_VARS =  { "XDG_DATA_HOME" : os.path.join (cfg.TEST_TMP_DIR, "xdg-data-home"),
+                   "XDG_CACHE_HOME": os.path.join (cfg.TEST_TMP_DIR, "xdg-cache-home")} 
+EXTRA_DIRS = [os.path.join (cfg.TEST_TMP_DIR, "xdg-data-home", "tracker"),
+              os.path.join (cfg.TEST_TMP_DIR, "xdg-cache-home", "tracker")]
+
+# This variable is not in the dictionary because not all tests need to modify it!
+XDG_CONFIG_HOME_DIR = os.path.join (cfg.TEST_TMP_DIR, "xdg-config-home")
+
+REASONABLE_TIMEOUT = 30
+
+class TrackerMinerFsLifeCycle():
+    """
+    Starts and monitors the miner-fs life cycle
+    """
+    def __init__ (self):
+        self.timeout_id = 0
+    
+    def start (self):
+        """
+        call this method to start and instance of miner-fs. It will return when the miner is 'Idle'
+        after all the initial crawling
+        """
+        self.loop = gobject.MainLoop()
+        dbus_loop = DBusGMainLoop(set_as_default=True)
+        self.bus = dbus.SessionBus (dbus_loop)
+
+        obj = self.bus.get_object ("org.freedesktop.DBus",
+                                   "/org/freedesktop/DBus")
+        self.admin = dbus.Interface (obj, dbus_interface="org.freedesktop.DBus")
+        if (self.admin.NameHasOwner (cfg.MINERFS_BUSNAME)):
+            raise Exception ("Miner is already running! kill it before starting this one")
+
+        self.name_owner_match = self.bus.add_signal_receiver (self.__name_owner_changed_cb,
+                                                         signal_name="NameOwnerChanged",
+                                                         path="/org/freedesktop/DBus",
+                                                         dbus_interface="org.freedesktop.DBus")
+        self.__start_tracker_miner_fs ()
+        
+        # It should step out of this loop when the miner is visible in DBus
+        self.loop.run ()
+
+        self.status_match = self.bus.add_signal_receiver (self.__minerfs_status_cb,
+                                                     signal_name="Progress",
+                                                     path=cfg.MINERFS_OBJ_PATH,
+                                                     dbus_interface=cfg.MINER_IFACE)
+        # It should step out of this loop after to "Idle" progress changes
+        self.timeout_id = glib.timeout_add_seconds (REASONABLE_TIMEOUT, self.__timeout_on_idle)
+        self.loop.run ()
+
+        
+    def stop (self):
+        self.__stop_tracker_miner_fs ()
+        # It should step out of this loop when the miner disappear from the bus
+        self.timeout_id = glib.timeout_add_seconds (REASONABLE_TIMEOUT, self.__timeout_on_idle)
+        self.loop.run ()
+
+        # Disconnect the signals of the next start we get duplicated messages
+        self.bus._clean_up_signal_match (self.name_owner_match)
+        self.bus._clean_up_signal_match (self.status_match)
+
+
+    def wait_for_idle (self, timeout=REASONABLE_TIMEOUT):
+        # The signal is already connected
+        print "Waiting for Idle"
+        self.timeout_id = glib.timeout_add_seconds (timeout, self.__timeout_on_idle)
+        self.loop.run ()
+
+    def __timeout_on_idle (self):
+        print "Timeout... asumming idle"
+        self.loop.quit ()
+        return False
+
+    def __minerfs_status_cb (self, status, handle):
+        print "Miner status is now", status.encode ("utf-8")
+        if (status == "Idle"):
+            if (self.timeout_id != 0):
+                glib.source_remove (self.timeout_id)
+                self.timeout_id = 0
+            self.loop.quit ()
+
+
+    def __name_owner_changed_cb (self, name, old_owner, new_owner):
+        if name == cfg.MINERFS_BUSNAME:
+            print "Miner name change %s -> %s" % (old_owner, new_owner)
+            self.loop.quit ()
+
+    def __start_tracker_miner_fs (self):
+        miner_fs_binary = os.path.join (cfg.EXEC_PREFIX, "tracker-miner-fs")
+        FNULL = open ('/dev/null', 'w')
+        return subprocess.Popen ([miner_fs_binary], stdout=FNULL, stderr=FNULL)
+
+    def __stop_tracker_miner_fs (self):
+        control_binary = os.path.join (cfg.BINDIR, "tracker-control")
+        FNULL = open('/dev/null', 'w')
+        subprocess.call ([control_binary, "-t"], stdout=FNULL)
+
+
+class TrackerWritebackLifeCycle():
+    """
+    Starts and monitors the writeback life cycle
+    """
+    def __init__ (self):
+        self.timeout_id = 0
+    
+    def start (self):
+        """
+        call this method to start and instance of writeback.
+        It will return when the Writeback object is visible in dbus
+        """
+        self.loop = gobject.MainLoop()
+        dbus_loop = DBusGMainLoop(set_as_default=True)
+        bus = dbus.SessionBus (dbus_loop)
+
+        obj = bus.get_object ("org.freedesktop.DBus",
+                              "/org/freedesktop/DBus")
+        self.admin = dbus.Interface (obj, dbus_interface="org.freedesktop.DBus")
+        if (self.admin.NameHasOwner (cfg.WRITEBACK_BUSNAME)):
+            raise Exception ("Writeback is already running! kill it before starting this one")
+
+        bus.add_signal_receiver (self.__name_owner_changed_cb,
+                                 signal_name="NameOwnerChanged",
+                                 path="/org/freedesktop/DBus",
+                                 dbus_interface="org.freedesktop.DBus")
+        self.__start_tracker_writeback ()
+
+        # It should step out of this loop when the writeback is visible in DBus
+        self.timeout_id = glib.timeout_add_seconds (REASONABLE_TIMEOUT, self.__timeout_on_idle)
+        self.loop.run ()
+        
+    def stop (self):
+        assert self.process 
+        self.process.kill ()
+
+    def __name_owner_changed_cb (self, name, old_owner, new_owner):
+        if name == cfg.WRITEBACK_BUSNAME:
+            print "Writeback changed named '%s' -> '%s'" % (old_owner, new_owner)
+            if (self.timeout_id != 0):
+                glib.source_remove (self.timeout_id)
+                self.timeout_id = 0
+            self.loop.quit ()
+            
+    def __timeout_on_idle (self):
+        print "Timeout... asumming idle"
+        self.loop.quit ()
+        return False
+
+    def __start_tracker_writeback (self):
+        writeback_binary = os.path.join (cfg.EXEC_PREFIX, "tracker-writeback")
+        writeback = [writeback_binary]
+        # The env variables can be passed as parameters!
+        FNULL = open('/dev/null', 'w')
+        self.process = subprocess.Popen (writeback, stdout=FNULL, stderr=FNULL)
+
+
+class TrackerSystemAbstraction:
+
+    def set_up_environment (self, confdir):
+        """
+        Sets up the XDG_*_HOME variables and make sure the directories exist
+        """
+        for var, directory in TEST_ENV_VARS.iteritems ():
+            print "Setting %s - %s" %(var, directory)
+            self.__recreate_directory (directory)
+            os.environ [var] = directory
+
+        for directory in EXTRA_DIRS:
+            self.__recreate_directory (directory)
+            
+        if confdir :
+            self.__recreate_directory (XDG_CONFIG_HOME_DIR)
+            shutil.copytree (os.path.join (confdir, "tracker"),
+                             os.path.join (XDG_CONFIG_HOME_DIR, "tracker"))
+            print "Setting %s - %s" % ("XDG_CONFIG_HOME", XDG_CONFIG_HOME_DIR)
+            print "  taking configuration from", confdir
+            os.environ ["XDG_CONFIG_HOME"] = XDG_CONFIG_HOME_DIR
+
+    def unset_up_environment (self):
+        """
+        Unset the XDG_*_HOME variables from the environment
+        """
+        for var, directory in TEST_ENV_VARS.iteritems ():
+            if os.environ.has_key (var):
+                del os.environ [var]
+                
+        if (os.environ.has_key ("XDG_CONFIG_HOME")):
+            del os.environ ["XDG_CONFIG_HOME"]
+        
+
+    def tracker_store_testing_start (self, confdir=None):
+        """
+        Stops any previous instance of the store, calls set_up_environment,
+        and starts a new instances of the store
+        """
+        self.__stop_tracker_processes ()
+        self.set_up_environment (confdir)
+        
+        self.tracker_running = self.__start_tracker_store ()
+
+    def tracker_store_brutal_restart (self):
+        self.tracker_running.kill ()
+        time.sleep (1)
+        self.tracker_running = self.__start_tracker_store ()
+        time.sleep (1)
+
+    def tracker_store_remove_dbs (self):
+        db_location = os.path.join (TEST_ENV_VARS ['XDG_CACHE_HOME'], "tracker")
+        shutil.rmtree (db_location)
+        os.mkdir (db_location)
+
+    def tracker_store_corrupt_dbs (self):
+        db_path = os.path.join (TEST_ENV_VARS ['XDG_CACHE_HOME'], "tracker", "meta.db")
+        f = open (db_path, "w")
+        for i in range (0, 100):
+            f.write ("Some stupid content... hohohoho, not a sqlite file anymore!\n")
+        f.close ()
+
+    def tracker_store_remove_journal (self):
+        db_location = os.path.join (TEST_ENV_VARS ['XDG_DATA_HOME'], "tracker", "data")
+        shutil.rmtree (db_location)
+        os.mkdir (db_location)
+
+    def tracker_store_testing_stop (self):
+        """
+        Stops a running tracker-store and unset all the XDG_*_HOME vars
+        """
+        self.__stop_tracker_processes ()
+        self.unset_up_environment ()
+
+
+    def tracker_miner_fs_testing_start (self, confdir=None):
+        """
+        Stops any previous instance of the store and miner, calls set_up_environment,
+        and starts a new instance of the store and miner-fs
+        """
+        self.__stop_tracker_processes ()
+        self.set_up_environment (confdir)
+
+        self.tracker_running = self.__start_tracker_store ()
+        self.miner_fs = TrackerMinerFsLifeCycle ()
+        self.miner_fs.start ()
+
+    def tracker_miner_fs_wait_for_idle (self, timeout=REASONABLE_TIMEOUT):
+        """
+        Copy the files physically in the filesyste and wait for the miner to complete the work
+        """
+        self.miner_fs.wait_for_idle (timeout)
+        time.sleep (1)
+
+
+    def tracker_miner_fs_testing_stop (self):
+        """
+        Stops the miner-fs and store running and unset all the XDG_*_HOME vars
+        """
+        self.miner_fs.stop ()
+        self.__stop_tracker_processes ()
+        self.unset_up_environment ()
+
+
+    def tracker_writeback_testing_start (self, confdir=None):
+        # Start the miner-fs (and store) and then the writeback process
+        self.tracker_miner_fs_testing_start (confdir)
+        self.writeback = TrackerWritebackLifeCycle ()
+        self.writeback.start ()
+
+    def tracker_writeback_testing_stop (self):
+        # Tracker write must have been started before
+        self.writeback.stop ()
+        self.tracker_miner_fs_testing_stop ()
+        
+    #
+    # Private API
+    #
+    def __start_tracker_store (self):
+        tracker_binary = os.path.join (cfg.EXEC_PREFIX, "tracker-store")
+        tracker = [tracker_binary]
+        # The env variables can be passed as parameters!
+        FNULL = open('/dev/null', 'w')
+        return subprocess.Popen (tracker, stdout=FNULL, stderr=FNULL)
+
+
+    def __stop_tracker_processes (self):
+        control_binary = os.path.join (cfg.BINDIR, "tracker-control")
+        FNULL = open('/dev/null', 'w')
+        subprocess.call ([control_binary, "-t"], stdout=FNULL)
+        time.sleep (1)
+
+
+
+    def __recreate_directory (self, directory):
+        if (os.path.exists (directory)):
+            shutil.rmtree (directory)
+        os.makedirs (directory)
+
+
+if __name__ == "__main__":
+    import gtk, glib, time
+
+    def destroy_the_world (a):
+        a.tracker_store_testing_stop ()
+        print "   stopped"
+        gtk.main_quit()
+
+    print "-- Starting store --"
+    a = TrackerSystemAbstraction ()
+    a.tracker_store_testing_start ()
+    print "   started, waiting 5 sec. to stop it"
+    glib.timeout_add_seconds (5, destroy_the_world, a)
+    gtk.main ()
+    
+    print "-- Starting miner-fs --"
+    b = TrackerMinerFsLifeCycle ()
+    b.start ()
+    print "  started, waiting 3 secs. to stop it"
+    time.sleep (3)
+    b.stop ()
+    print "  stopped"



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]