400-extractor-metadata.py 10.7 KB
Newer Older
Ivan Frade's avatar
Ivan Frade committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
#!/usr/bin/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.
#
"""
For a collection of files, call the extractor and check that the expected
metadata is extracted. Load dynamically the test information from a data
directory (containing xxx.expected files)
"""
25

Ivan Frade's avatar
Ivan Frade committed
26
from common.utils import configuration as cfg
27
from common.utils.extractor import get_tracker_extract_output
Ivan Frade's avatar
Ivan Frade committed
28 29 30 31 32 33
import unittest2 as ut
import os
import sys

import ConfigParser

34

Ivan Frade's avatar
Ivan Frade committed
35 36
class ExtractionTestCase (ut.TestCase):
    """
37
    Test checks if the tracker extractor is able to retrieve metadata
Ivan Frade's avatar
Ivan Frade committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
    """
    def __init__ (self, methodName='runTest', descfile=None):
        """
        Descfile is the description file in a relative path
        """
        ut.TestCase.__init__ (self, methodName)

        # Load the description file
        assert descfile
        self.rel_description = descfile
        self.configParser = self.__load_description_file (self.rel_description)

        # Add a method to the class called after the description file
        methodName = self.rel_description.lower()[:-len(".expected")].replace (" ", "_")[-60:]

        if (self.__is_expected_failure ()):
            setattr (self,
                     methodName,
                     self.expected_failure_test_extraction)
        else:
            setattr (self,
                     methodName,
                     self.generic_test_extraction)

        # unittest framework will run the test called "self._testMethodName"
        # So we set that variable to our new name
        self._testMethodName = methodName

    def runTest (self):
        """
        Empty function pointer, that should NEVER be called. It is required to exist by unittest.
        """
        assert False

    def __load_description_file (self, descfile):
        configParser = ConfigParser.RawConfigParser ()
        # Make it case sensitive:
        configParser.optionxform = str
76

Ivan Frade's avatar
Ivan Frade committed
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
        abs_description = os.path.abspath (descfile)
        loaded_files = configParser.read (abs_description)
        if not abs_description in loaded_files:
            raise Exception("Unable to load %s" % (abs_description))

        return configParser

    def __is_expected_failure (self):
        assert self.configParser
        return self.configParser.has_option ("TestFile", "ExpectedFailure")

    def __get_bugnumber (self):
        assert self.configParser
        if self.configParser.has_option ("TestFile", "Bugzilla"):
            return "'" + self.configParser.get ("TestFile", "Bugzilla") + "'"
        else:
            return None
94

Ivan Frade's avatar
Ivan Frade committed
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
    def expected_failure_test_extraction (self):
        try:
            self.generic_test_extraction ()
        except Exception:
            raise ut.case._ExpectedFailure(sys.exc_info())

        if self.__get_bugnumber ():
            raise Exception ("Unexpected success. Maybe bug: " + self.__get_bugnumber () + " has been fixed?")
        else:
            raise Exception ("Unexpected success. Check " + self.rel_description)

    def generic_test_extraction (self):
        abs_description = os.path.abspath (self.rel_description)

        # Filename contains the file to extract, in a relative path to the description file
        desc_root, desc_file = os.path.split (abs_description)
111

112 113
        filename_to_extract = self.configParser.get ("TestFile", "Filename")
        self.file_to_extract = os.path.join (desc_root, filename_to_extract)
Ivan Frade's avatar
Ivan Frade committed
114

115 116
        result = get_tracker_extract_output(self.file_to_extract)
        self.__assert_extraction_ok (result)
Ivan Frade's avatar
Ivan Frade committed
117 118 119 120 121 122 123

    def assertDictHasKey (self, d, key, msg=None):
        if not d.has_key (key):
            standardMsg = "Missing: %s\n" % (key)
            self.fail (self._formatMessage (msg, standardMsg))
        else:
            return
124 125 126 127 128

    def assertIsURN (self, supposed_uuid, msg=None):
        import uuid

        try:
129 130 131
            if (supposed_uuid.startswith ("<") and supposed_uuid.endswith (">")):
                supposed_uuid = supposed_uuid[1:-1]

132 133 134 135
            uuid.UUID (supposed_uuid)
        except ValueError:
            standardMsg = "'%s' is not a valid UUID" % (supposed_uuid)
            self.fail (self._formatMessage (msg, standardMsg))
136

Ivan Frade's avatar
Ivan Frade committed
137 138 139 140 141 142 143 144 145
    def __assert_extraction_ok (self, result):
        self.__check_section ("Metadata", result)

    def __check_section (self, section, result):
        error_missing_prop = "Property '%s' hasn't been extracted from file \n'%s'\n (requested on '%s' [%s])"
        error_wrong_value = "on property '%s' from file %s\n (requested on: '%s' [%s])"
        error_extra_prop = "Property '%s' was explicitely banned for file \n'%s'\n (requested on '%s' [%s])"
        error_extra_prop_v = "Property '%s' with value '%s' was explicitely banned for file \n'%s'\n (requested on %s' [%s])"

146 147 148 149 150 151 152 153 154 155 156 157
        expected_pairs = [] # List of expected (key, value)
        unexpected_pairs = []  # List of unexpected (key, value)
        expected_keys = []  # List of expected keys (the key must be there, value doesnt matter)

        for k, v in self.configParser.items (section):
            if k.startswith ("!"):
                unexpected_pairs.append ( (k[1:].replace ("_", ":"), v) )
            elif k.startswith ("@"):
                expected_keys.append ( k[1:].replace ("_", ":") )
            else:
                expected_pairs.append ( (k.replace ("_", ":"), v) )

Ivan Frade's avatar
Ivan Frade committed
158 159 160 161 162 163 164

        for (prop, value) in expected_pairs:
            self.assertDictHasKey (result, prop,
                                   error_missing_prop % (prop,
                                                         self.file_to_extract,
                                                         self.rel_description,
                                                         section))
165 166 167 168 169 170 171 172 173 174 175 176 177
            if value == "@URNUUID@":
                # Watch out! We take only the FIRST element. Incompatible with multiple-valued props.
                self.assertIsURN (result [prop][0],
                                  error_wrong_value % (prop,
                                                       self.file_to_extract,
                                                       self.rel_description,
                                                       section))
            else:
                self.assertIn (value, result [prop],
                               error_wrong_value % (prop,
                                                    self.file_to_extract,
                                                    self.rel_description,
                                                    section))
Ivan Frade's avatar
Ivan Frade committed
178 179 180 181 182 183 184 185 186

        for (prop, value) in unexpected_pairs:
            # There is no prop, or it is but not with that value
            if (value == ""):
                self.assertFalse (result.has_key (prop), error_extra_prop % (prop,
                                                                             self.file_to_extract,
                                                                             self.rel_description,
                                                                             section))
            else:
187 188 189 190 191 192 193 194 195 196 197
                if (value == "@URNUUID@"):
                    self.assertIsURN (result [prop][0], error_extra_prop % (prop,
                                                                            self.file_to_extract,
                                                                            self.rel_description,
                                                                            section))
                else:
                    self.assertNotIn (value, result [prop], error_extra_prop_v % (prop,
                                                                                  value,
                                                                                  self.file_to_extract,
                                                                                  self.rel_description,
                                                                                  section))
Ivan Frade's avatar
Ivan Frade committed
198

199 200 201 202 203 204 205
        for prop in expected_keys:
             self.assertDictHasKey (result, prop,
                                    error_missing_prop % (prop,
                                                          self.file_to_extract,
                                                          self.rel_description,
                                                          section))

206

207
def run_all ():
Ivan Frade's avatar
Ivan Frade committed
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
    ##
    # Traverse the TEST_DATA_PATH directory looking for .description files
    # Add a new TestCase to the suite per .description file and run the suite.
    #
    # Is we do this inside a single TestCase an error in one test would stop the whole
    # testing.
    ##
    if (os.path.exists (os.getcwd() + "/test-extraction-data")):
        # Use local directory if available
        TEST_DATA_PATH = os.getcwd() + "/test-extraction-data"
    else:
        TEST_DATA_PATH = os.path.join (cfg.DATADIR, "tracker-tests",
                                       "test-extraction-data")
    print "Loading test descriptions from", TEST_DATA_PATH
    extractionTestSuite = ut.TestSuite ()
    for root, dirs, files in os.walk (TEST_DATA_PATH):
         descriptions = [os.path.join (root, f) for f in files if f.endswith ("expected")]
         for descfile in descriptions:
             tc = ExtractionTestCase(descfile=descfile)
             extractionTestSuite.addTest(tc)
    result = ut.TextTestRunner (verbosity=1).run (extractionTestSuite)
    sys.exit(not result.wasSuccessful())

231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
def run_one (filename):
    ##
    # Run just one .description file
    ##
    description = os.path.join (os.getcwd (), filename) 

    extractionTestSuite = ut.TestSuite ()
    tc = ExtractionTestCase(descfile=description)
    extractionTestSuite.addTest(tc)

    result = ut.TextTestRunner (verbosity=2).run (extractionTestSuite)
    sys.exit(not result.wasSuccessful())


if __name__ == "__main__":
    if (len (sys.argv) == 1):
        run_all ()
    else:
        if os.path.exists (sys.argv[1]) and sys.argv[1].endswith (".expected"):
            run_one (sys.argv[1])
251 252 253
        # FIXME: for the case when invoked by testrunner (see create-tests-xml.py)
        elif sys.argv[1] == "ExtractionTestCase":
            run_all ()
254
        else:
255
            print "Usage: %s [FILE.expected]" % (sys.argv[0])
256