python: Add bindings for xml_parse functions + base tests.
authorTomas Mlcoch <tmlcoch@redhat.com>
Fri, 31 May 2013 12:11:56 +0000 (14:11 +0200)
committerTomas Mlcoch <tmlcoch@redhat.com>
Fri, 31 May 2013 12:41:52 +0000 (14:41 +0200)
src/python/__init__.py
src/python/createrepo_cmodule.c
src/python/typeconversion.c
src/python/typeconversion.h
src/python/xml_parser-py.c
src/python/xml_parser-py.h
tests/python/tests/fixtures.py
tests/python/tests/test_xml_parser.py [new file with mode: 0644]

index 2febf35..2eafaa6 100644 (file)
@@ -100,16 +100,15 @@ class OtherXmlFile(XmlFile):
     def __init__(self, filename, compressiontype=GZ_COMPRESSION):
         XmlFile.__init__(self, filename, XMLFILE_OTHER, compressiontype)
 
-# XmlParser class
-
-XmlParser = _createrepo_c.XmlParser
-
 # Methods
 
-xml_dump_primary = _createrepo_c.xml_dump_primary
-xml_dump_filelists = _createrepo_c.xml_dump_filelists
-xml_dump_other = _createrepo_c.xml_dump_other
-xml_dump = _createrepo_c.xml_dump
+xml_dump_primary    = _createrepo_c.xml_dump_primary
+xml_dump_filelists  = _createrepo_c.xml_dump_filelists
+xml_dump_other      = _createrepo_c.xml_dump_other
+xml_dump            = _createrepo_c.xml_dump
+xml_parse_primary   = _createrepo_c.xml_parse_primary
+xml_parse_filelists = _createrepo_c.xml_parse_filelists
+xml_parse_other     = _createrepo_c.xml_parse_other
 
 def package_from_rpm(filename, checksum_type=SHA256, location_href=None,
                      location_base=None, changelog_limit=10):
index 50e6200..c8e6d66 100644 (file)
@@ -46,6 +46,12 @@ static struct PyMethodDef createrepo_c_methods[] = {
      METH_VARARGS, NULL},
     {"xml_dump",                (PyCFunction)py_xml_dump,
      METH_VARARGS, NULL},
+    {"xml_parse_primary",       (PyCFunction)py_xml_parse_primary,
+     METH_VARARGS, NULL},
+    {"xml_parse_filelists",     (PyCFunction)py_xml_parse_filelists,
+     METH_VARARGS, NULL},
+    {"xml_parse_other",         (PyCFunction)py_xml_parse_other,
+     METH_VARARGS, NULL},
     { NULL }
 };
 
@@ -105,12 +111,6 @@ init_createrepo_c(void)
     Py_INCREF(&XmlFile_Type);
     PyModule_AddObject(m, "XmlFile", (PyObject *)&XmlFile_Type);
 
-    /* _createrepo_c.XmlParser */
-    if (PyType_Ready(&XmlParser_Type) < 0)
-        return;
-    Py_INCREF(&XmlParser_Type);
-    PyModule_AddObject(m, "XmlParser", (PyObject *)&XmlParser_Type);
-
     /* Createrepo init */
 
     cr_xml_dump_init();
index 6b778cd..1c5279a 100644 (file)
 #include <assert.h>
 #include "typeconversion.h"
 
+void
+PyErr_ToGError(GError **err)
+{
+    PyObject *type, *val, *traceback, *pystr;
+
+    if (!err)
+        return;
+
+    assert(*err == NULL);
+
+    PyErr_Fetch(&type, &val, &traceback);
+
+    pystr = PyObject_Str(val);
+
+    Py_XDECREF(type);
+    Py_XDECREF(val);
+    Py_XDECREF(traceback);
+
+    if (!pystr) {
+        PyErr_Clear();
+        g_set_error(err, CR_XML_PARSER_ERROR, CRE_XMLPARSER,
+                    "Error while error handling");
+    } else {
+        g_set_error(err, CR_XML_PARSER_ERROR, CRE_XMLPARSER,
+                    PyString_AsString(pystr));
+    }
+
+    Py_XDECREF(pystr);
+}
+
 PyObject *
 PyStringOrNone_FromString(const char *str)
 {
index c8492a0..9139e4f 100644 (file)
@@ -23,6 +23,9 @@
 #include <glib.h>
 #include "src/createrepo_c.h"
 
+// Clears the current Python Exception and return its representation in GError
+void PyErr_ToGError(GError **err);
+
 PyObject *PyStringOrNone_FromString(const char *str);
 char *PyObject_ToStrOrNull(PyObject *pyobj);
 
index 4567c05..c4f6f5c 100644 (file)
 
 #include "src/createrepo_c.h"
 
+#include "xml_parser-py.h"
 #include "typeconversion.h"
 #include "package-py.h"
 #include "exception-py.h"
 
-typedef struct {
-    PyObject_HEAD
-    PyObject *newpkgcb;
-    PyObject *pkgcb;
-    PyObject *warningcb;
-} _XmlParserObject;
-
-static int
-check_XmlParserStatus(const _XmlParserObject *self)
-{
-    assert(self != NULL);
-    assert(XmlParserObject_Check(self));
-    return 0;
-}
-
-/* Functions on the type */
-
-static PyObject *
-xmlparser_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
-{
-    CR_UNUSED(args);
-    CR_UNUSED(kwds);
-    _XmlParserObject *self = (_XmlParserObject *)type->tp_alloc(type, 0);
-    if (self) {
-        self->newpkgcb  = NULL;
-        self->pkgcb     = NULL;
-        self->warningcb = NULL;
-    }
-    return (PyObject *)self;
-}
-
-static int
-xmlparser_init(_XmlParserObject *self, PyObject *args, PyObject *kwds)
-{
-    CR_UNUSED(args);
-    CR_UNUSED(kwds);
-
-    //if (!PyArg_ParseTuple(args, "|:xmlparser_init"))
-    //    return -1;
-
-    /* Free all previous resources when reinitialization */
-    Py_XDECREF(self->newpkgcb);
-    Py_XDECREF(self->pkgcb);
-    Py_XDECREF(self->warningcb);
-
-    self->newpkgcb  = NULL;
-    self->pkgcb     = NULL;
-    self->warningcb = NULL;
-
-    return 0;
-}
-
-static void
-xmlparser_dealloc(_XmlParserObject *self)
-{
-    Py_XDECREF(self->newpkgcb);
-    Py_XDECREF(self->pkgcb);
-    Py_XDECREF(self->warningcb);
-    Py_TYPE(self)->tp_free(self);
-}
-
-static PyObject *
-xmlparser_repr(_XmlParserObject *self)
-{
-    return PyString_FromFormat("<createrepo_c.XmlParser object>");
-}
-
-/* XmlParser methods */
-
 static int
 c_newpkgcb(cr_Package **pkg,
            const char *pkgId,
@@ -109,19 +41,43 @@ c_newpkgcb(cr_Package **pkg,
     arglist = Py_BuildValue("(sss)", pkgId, name, arch);
     result = PyObject_CallObject(cbdata, arglist);
     Py_DECREF(arglist);
-    // XXX Process and decref result
+
+    if (result == NULL) {
+        // Exception raised
+        PyErr_ToGError(err);
+        return CR_CB_RET_ERR;
+    }
+
+    if (!PackageObject_Check(result) && result != Py_None) {
+        PyErr_SetString(PyExc_TypeError,
+            "Expected a cr_Package or None as a callback return value");
+        return CR_CB_RET_ERR;
+    }
+
+    *pkg = Package_FromPyObject(result);
+
+    Py_DECREF(result);
     return CR_CB_RET_OK;
 }
 
 static int
-c_pkgcb(cr_Package *pkg, void *cbdata, GError **err)
+c_pkgcb(cr_Package *pkg,
+        void *cbdata,
+        GError **err)
 {
-    PyObject *py_pkg, *arglist, *result;
+    PyObject *result;
 
-    arglist = Py_BuildValue("(O)", py_pkg);
-    result = PyObject_CallObject(cbdata, arglist);
-    Py_DECREF(arglist);
-    // XXX Process and decref result
+    CR_UNUSED(pkg);
+
+    result = PyObject_CallObject(cbdata, NULL);
+
+    if (result == NULL) {
+        // Exception raised
+        PyErr_ToGError(err);
+        return CR_CB_RET_ERR;
+    }
+
+    Py_DECREF(result);
     return CR_CB_RET_OK;
 }
 
@@ -136,7 +92,14 @@ c_warningcb(cr_XmlParserWarningType type,
     arglist = Py_BuildValue("(is)", type, msg);
     result = PyObject_CallObject(cbdata, arglist);
     Py_DECREF(arglist);
-    // XXX Process and decref result
+
+    if (result == NULL) {
+        // Exception raised
+        PyErr_ToGError(err);
+        return CR_CB_RET_ERR;
+    }
+
+    Py_DECREF(result);
     return CR_CB_RET_OK;
 }
 
@@ -159,56 +122,113 @@ py_xml_parse_primary(PyObject *self, PyObject *args)
         return NULL;
     }
 
-    if (!PyCallable_Check(newpkgcb) || newpkgcb != Py_None) {
-        PyErr_SetString(PyExc_TypeError, "newpkgcb must be callable or None");
+    if (!PyCallable_Check(newpkgcb)) {
+        PyErr_SetString(PyExc_TypeError, "newpkgcb must be callable");
         return NULL;
     }
 
-    if (!PyCallable_Check(pkgcb) || pkgcb != Py_None) {
+    if (!PyCallable_Check(pkgcb) && pkgcb != Py_None) {
         PyErr_SetString(PyExc_TypeError, "pkgcb must be callable or None");
         return NULL;
     }
 
-    if (!PyCallable_Check(warningcb) || warningcb != Py_None) {
+    if (!PyCallable_Check(warningcb) && warningcb != Py_None) {
         PyErr_SetString(PyExc_TypeError, "warningcb must be callable or None");
         return NULL;
     }
 
-    /*
-    Py_XDECREF(self->newpkgcb);
-    Py_XDECREF(self->pkgcb);
-    Py_XDECREF(self->warningcb);
-    */
+    Py_XINCREF(newpkgcb);
+    Py_XINCREF(pkgcb);
+    Py_XINCREF(warningcb);
 
-    cr_XmlParserNewPkgCb    *ptr_c_newpkgcb  = NULL;
-    cr_XmlParserPkgCb       *ptr_c_pkgcb     = NULL;
-    cr_XmlParserWarningCb   *ptr_c_warningcb = NULL;
+    cr_XmlParserPkgCb       ptr_c_pkgcb     = NULL;
+    cr_XmlParserWarningCb   ptr_c_warningcb = NULL;
 
-    if (newpkgcb != Py_None)
-        ptr_c_newpkgcb = c_newpkgcb;
     if (pkgcb != Py_None)
         ptr_c_pkgcb = c_pkgcb;
-    if (newpkgcb != Py_None)
+    if (warningcb != Py_None)
         ptr_c_warningcb = c_warningcb;
 
-    /*
-    self->newpkgcb  = (newpkgcb == Py_None) ? NULL : newpkgcb;
-    Py_XINCREF(self->newpkgcb);
-    self->pkgcb     = (pkgcb == Py_None) ? NULL : pkgcb;
-    Py_XINCREF(self->pkgcb);
-    self->warningcb = (warningcb == Py_None) ? NULL : warningcb;
-    Py_XINCREF(self->warningcb);
-    */
-
     cr_xml_parse_primary(filename,
                          c_newpkgcb,
-                         &newpkgcb,
-                         c_pkgcb,
-                         &pkgcb,
-                         c_warningcb,
-                         &warningcb,
+                         newpkgcb,
+                         ptr_c_pkgcb,
+                         pkgcb,
+                         ptr_c_warningcb,
+                         warningcb,
                          do_files,
                          &tmp_err);
+
+    Py_XDECREF(newpkgcb);
+    Py_XDECREF(pkgcb);
+    Py_XDECREF(warningcb);
+
+    if (tmp_err) {
+        PyErr_Format(CrErr_Exception, "%s", tmp_err->message);
+        g_clear_error(&tmp_err);
+        return NULL;
+    }
+
+    Py_RETURN_NONE;
+}
+
+PyObject *
+py_xml_parse_filelists(PyObject *self, PyObject *args)
+{
+    CR_UNUSED(self);
+
+    char *filename;
+    PyObject *newpkgcb, *pkgcb, *warningcb;
+    GError *tmp_err = NULL;
+
+    if (!PyArg_ParseTuple(args, "sOOO:py_xml_parse_filelists",
+                                         &filename,
+                                         &newpkgcb,
+                                         &pkgcb,
+                                         &warningcb)) {
+        return NULL;
+    }
+
+    if (!PyCallable_Check(newpkgcb)) {
+        PyErr_SetString(PyExc_TypeError, "newpkgcb must be callable");
+        return NULL;
+    }
+
+    if (!PyCallable_Check(pkgcb) && pkgcb != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "pkgcb must be callable or None");
+        return NULL;
+    }
+
+    if (!PyCallable_Check(warningcb) && warningcb != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "warningcb must be callable or None");
+        return NULL;
+    }
+
+    Py_XINCREF(newpkgcb);
+    Py_XINCREF(pkgcb);
+    Py_XINCREF(warningcb);
+
+    cr_XmlParserPkgCb       ptr_c_pkgcb     = NULL;
+    cr_XmlParserWarningCb   ptr_c_warningcb = NULL;
+
+    if (pkgcb != Py_None)
+        ptr_c_pkgcb = c_pkgcb;
+    if (warningcb != Py_None)
+        ptr_c_warningcb = c_warningcb;
+
+    cr_xml_parse_filelists(filename,
+                           c_newpkgcb,
+                           newpkgcb,
+                           ptr_c_pkgcb,
+                           pkgcb,
+                           ptr_c_warningcb,
+                           warningcb,
+                           &tmp_err);
+
+    Py_XDECREF(newpkgcb);
+    Py_XDECREF(pkgcb);
+    Py_XDECREF(warningcb);
+
     if (tmp_err) {
         PyErr_Format(CrErr_Exception, "%s", tmp_err->message);
         g_clear_error(&tmp_err);
@@ -218,51 +238,68 @@ py_xml_parse_primary(PyObject *self, PyObject *args)
     Py_RETURN_NONE;
 }
 
-static struct PyMethodDef xmlparser_methods[] = {
-    {"parse_primary", (PyCFunction)py_xml_parse_primary, METH_VARARGS, NULL},
-    {NULL} /* sentinel */
-};
-
-PyTypeObject XmlParser_Type = {
-    PyObject_HEAD_INIT(NULL)
-    0,                              /* ob_size */
-    "_librepo.XmlParser",           /* tp_name */
-    sizeof(_XmlParserObject),       /* tp_basicsize */
-    0,                              /* tp_itemsize */
-    (destructor) xmlparser_dealloc, /* tp_dealloc */
-    0,                              /* tp_print */
-    0,                              /* tp_getattr */
-    0,                              /* tp_setattr */
-    0,                              /* tp_compare */
-    (reprfunc) xmlparser_repr,      /* tp_repr */
-    0,                              /* tp_as_number */
-    0,                              /* tp_as_sequence */
-    0,                              /* tp_as_mapping */
-    0,                              /* tp_hash */
-    0,                              /* tp_call */
-    0,                              /* tp_str */
-    0,                              /* tp_getattro */
-    0,                              /* tp_setattro */
-    0,                              /* tp_as_buffer */
-    Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */
-    "XmlParser object",             /* tp_doc */
-    0,                              /* tp_traverse */
-    0,                              /* tp_clear */
-    0,                              /* tp_richcompare */
-    0,                              /* tp_weaklistoffset */
-    PyObject_SelfIter,              /* tp_iter */
-    0,                              /* tp_iternext */
-    xmlparser_methods,              /* tp_methods */
-    0,                              /* tp_members */
-    0,                              /* tp_getset */
-    0,                              /* tp_base */
-    0,                              /* tp_dict */
-    0,                              /* tp_descr_get */
-    0,                              /* tp_descr_set */
-    0,                              /* tp_dictoffset */
-    (initproc) xmlparser_init,      /* tp_init */
-    0,                              /* tp_alloc */
-    xmlparser_new,                  /* tp_new */
-    0,                              /* tp_free */
-    0,                              /* tp_is_gc */
-};
+PyObject *
+py_xml_parse_other(PyObject *self, PyObject *args)
+{
+    CR_UNUSED(self);
+
+    char *filename;
+    PyObject *newpkgcb, *pkgcb, *warningcb;
+    GError *tmp_err = NULL;
+
+    if (!PyArg_ParseTuple(args, "sOOO:py_xml_parse_other",
+                                         &filename,
+                                         &newpkgcb,
+                                         &pkgcb,
+                                         &warningcb)) {
+        return NULL;
+    }
+
+    if (!PyCallable_Check(newpkgcb)) {
+        PyErr_SetString(PyExc_TypeError, "newpkgcb must be callable");
+        return NULL;
+    }
+
+    if (!PyCallable_Check(pkgcb) && pkgcb != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "pkgcb must be callable or None");
+        return NULL;
+    }
+
+    if (!PyCallable_Check(warningcb) && warningcb != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "warningcb must be callable or None");
+        return NULL;
+    }
+
+    Py_XINCREF(newpkgcb);
+    Py_XINCREF(pkgcb);
+    Py_XINCREF(warningcb);
+
+    cr_XmlParserPkgCb       ptr_c_pkgcb     = NULL;
+    cr_XmlParserWarningCb   ptr_c_warningcb = NULL;
+
+    if (pkgcb != Py_None)
+        ptr_c_pkgcb = c_pkgcb;
+    if (warningcb != Py_None)
+        ptr_c_warningcb = c_warningcb;
+
+    cr_xml_parse_other(filename,
+                       c_newpkgcb,
+                       newpkgcb,
+                       ptr_c_pkgcb,
+                       pkgcb,
+                       ptr_c_warningcb,
+                       warningcb,
+                       &tmp_err);
+
+    Py_XDECREF(newpkgcb);
+    Py_XDECREF(pkgcb);
+    Py_XDECREF(warningcb);
+
+    if (tmp_err) {
+        PyErr_Format(CrErr_Exception, "%s", tmp_err->message);
+        g_clear_error(&tmp_err);
+        return NULL;
+    }
+
+    Py_RETURN_NONE;
+}
index c40be48..9847b45 100644 (file)
@@ -22,8 +22,8 @@
 
 #include "src/createrepo_c.h"
 
-extern PyTypeObject XmlParser_Type;
-
-#define XmlParserObject_Check(o)    Pyobject_TypeCheck(o, &XmlParser_Type)
+PyObject *py_xml_parse_primary(PyObject *self, PyObject *args);
+PyObject *py_xml_parse_filelists(PyObject *self, PyObject *args);
+PyObject *py_xml_parse_other(PyObject *self, PyObject *args);
 
 #endif
index 901a20e..6d266cb 100644 (file)
@@ -35,10 +35,27 @@ PKG_SUPER_KERNEL_PATH = os.path.join(PACKAGES_PATH, PKG_SUPER_KERNEL)
 # Test repositories
 REPO_00_PATH = os.path.join(REPOS_PATH, "repo_00")
 REPO_00_PRIXML = os.path.join(REPO_00_PATH, "repodata/",
-                              "dabe2ce5481d23de1f4f52bdcfee0f9af98316c9e0de2ce8123adeefa0dd08b9-primary.xml.gz")
+    "dabe2ce5481d23de1f4f52bdcfee0f9af98316c9e0de2ce8123adeefa0dd08b9-primary.xml.gz")
+REPO_00_FILXML = os.path.join(REPO_00_PATH, "repodata/",
+    "401dc19bda88c82c403423fb835844d64345f7e95f5b9835888189c03834cc93-filelists.xml.gz")
+REPO_00_OTHXML = os.path.join(REPO_00_PATH, "repodata/",
+    "6bf9672d0862e8ef8b8ff05a2fd0208a922b1f5978e6589d87944c88259cb670-other.xml.gz")
 
 REPO_01_PATH = os.path.join(REPOS_PATH, "repo_01")
+REPO_01_PRIXML = os.path.join(REPO_01_PATH, "repodata/",
+    "6c662d665c24de9a0f62c17d8fa50622307739d7376f0d19097ca96c6d7f5e3e-primary.xml.gz")
+REPO_01_FILXML = os.path.join(REPO_01_PATH, "repodata/",
+    "c7db035d0e6f1b2e883a7fa3229e2d2be70c05a8b8d2b57dbb5f9c1a67483b6c-filelists.xml.gz")
+REPO_01_OTHXML = os.path.join(REPO_01_PATH, "repodata/",
+    "b752a73d9efd4006d740f943db5fb7c2dd77a8324bd99da92e86bd55a2c126ef-other.xml.gz")
+
 REPO_02_PATH = os.path.join(REPOS_PATH, "repo_02")
+REPO_02_PRIXML = os.path.join(REPO_02_PATH, "repodata/",
+    "bcde64b04916a2a72fdc257d61bc922c70b3d58e953499180585f7a360ce86cf-primary.xml.gz")
+REPO_02_FILXML = os.path.join(REPO_02_PATH, "repodata/",
+    "3b7e6ecd01af9cb674aff6458186911d7081bb5676d5562a21a963afc8a8bcc7-filelists.xml.gz")
+REPO_02_OTHXML = os.path.join(REPO_02_PATH, "repodata/",
+    "ab5d3edeea50f9b4ec5ee13e4d25c147e318e3a433dbabc94d3461f58ac28255-other.xml.gz")
 
 # Other test files
 
diff --git a/tests/python/tests/test_xml_parser.py b/tests/python/tests/test_xml_parser.py
new file mode 100644 (file)
index 0000000..0c4fe79
--- /dev/null
@@ -0,0 +1,296 @@
+import re
+import unittest
+import shutil
+import tempfile
+import os.path
+import createrepo_c as cr
+
+from fixtures import *
+
+class TestCaseXmlParserPrimary(unittest.TestCase):
+
+    def test_xml_parser_primary_repo01(self):
+
+        userdata = {
+                "pkgs": [],
+                "pkgcb_calls": 0,
+                "warnings": []
+            }
+
+        def newpkgcb(pkgId, name, arch):
+            pkg = cr.Package()
+            userdata["pkgs"].append(pkg)
+            return pkg
+
+        def pkgcb():
+            userdata["pkgcb_calls"] += 1
+
+        def warningcb(warn_type, msg):
+            userdata["warnings"].append((warn_type, msg))
+
+        cr.xml_parse_primary(REPO_01_PRIXML, newpkgcb, pkgcb, warningcb, 1)
+
+        self.assertEqual([pkg.name for pkg in userdata["pkgs"]],
+            ['super_kernel'])
+        self.assertEqual(userdata["pkgcb_calls"], 1)
+        self.assertEqual(userdata["warnings"], [])
+
+        pkg = userdata["pkgs"][0]
+        self.assertEqual(pkg.pkgId, "152824bff2aa6d54f429d43e87a3ff3a0286505c6d93ec87692b5e3a9e3b97bf")
+        self.assertEqual(pkg.name, "super_kernel")
+        self.assertEqual(pkg.arch, "x86_64")
+        self.assertEqual(pkg.version, "6.0.1")
+        self.assertEqual(pkg.epoch, "0")
+        self.assertEqual(pkg.release, "2")
+        self.assertEqual(pkg.summary, "Test package")
+        self.assertEqual(pkg.description, "This package has provides, requires, obsoletes, conflicts options.")
+        self.assertEqual(pkg.url, "http://so_super_kernel.com/it_is_awesome/yep_it_really_is")
+        self.assertEqual(pkg.time_file, 1334667003)
+        self.assertEqual(pkg.time_build, 1334667003)
+        self.assertEqual(pkg.rpm_license, "LGPLv2")
+        self.assertEqual(pkg.rpm_vendor, None)
+        self.assertEqual(pkg.rpm_group, "Applications/System")
+        self.assertEqual(pkg.rpm_buildhost, "localhost.localdomain")
+        self.assertEqual(pkg.rpm_sourcerpm, "super_kernel-6.0.1-2.src.rpm")
+        self.assertEqual(pkg.rpm_header_start, 280)
+        self.assertEqual(pkg.rpm_header_end, 2637)
+        self.assertEqual(pkg.rpm_packager, None)
+        self.assertEqual(pkg.size_package, 2845)
+        self.assertEqual(pkg.size_installed, 0)
+        self.assertEqual(pkg.size_archive, 404)
+        self.assertEqual(pkg.location_href, "super_kernel-6.0.1-2.x86_64.rpm")
+        self.assertEqual(pkg.location_base, None)
+        self.assertEqual(pkg.checksum_type, "sha256")
+        self.assertEqual(pkg.requires,
+                [('bzip2', 'GE', '0', '1.0.0', None, True),
+                 ('expat', None, None, None, None, True),
+                 ('glib', 'GE', '0', '2.26.0', None, False),
+                 ('zlib', None, None, None, None, False)])
+        self.assertEqual(pkg.provides,
+                [('not_so_super_kernel', 'LT', '0', '5.8.0', None, False),
+                 ('super_kernel', 'EQ', '0', '6.0.0', None, False),
+                 ('super_kernel', 'EQ', '0', '6.0.1', '2', False),
+                 ('super_kernel(x86-64)', 'EQ', '0', '6.0.1', '2', False)])
+        self.assertEqual(pkg.conflicts,
+                [('kernel', None, None, None, None, False),
+                 ('super_kernel', 'EQ', '0', '5.0.0', None, False),
+                 ('super_kernel', 'LT', '0', '4.0.0', None, False)])
+        self.assertEqual(pkg.obsoletes,
+                [('kernel', None, None, None, None, False),
+                 ('super_kernel', 'EQ', '0', '5.9.0', None, False)])
+        self.assertEqual(pkg.files,
+                [(None, '/usr/bin/', 'super_kernel')])
+        self.assertEqual(pkg.changelogs, [])
+
+
+    def test_xml_parser_primary_repo02(self):
+
+        userdata = {
+                "pkgs": [],
+                "pkgcb_calls": 0,
+                "warnings": []
+            }
+
+        def newpkgcb(pkgId, name, arch):
+            pkg = cr.Package()
+            userdata["pkgs"].append(pkg)
+            return pkg
+
+        def pkgcb():
+            userdata["pkgcb_calls"] += 1
+
+        def warningcb(warn_type, msg):
+            userdata["warnings"].append((warn_type, msg))
+
+        cr.xml_parse_primary(REPO_02_PRIXML, newpkgcb, pkgcb, warningcb, 1)
+
+        self.assertEqual([pkg.name for pkg in userdata["pkgs"]],
+            ['fake_bash', 'super_kernel'])
+        self.assertEqual(userdata["pkgcb_calls"], 2)
+        self.assertEqual(userdata["warnings"], [])
+
+class TestCaseXmlParserFilelists(unittest.TestCase):
+
+    def test_xml_parser_filelists_repo01(self):
+
+        userdata = {
+                "pkgs": [],
+                "pkgcb_calls": 0,
+                "warnings": []
+            }
+
+        def newpkgcb(pkgId, name, arch):
+            pkg = cr.Package()
+            userdata["pkgs"].append(pkg)
+            return pkg
+
+        def pkgcb():
+            userdata["pkgcb_calls"] += 1
+
+        def warningcb(warn_type, msg):
+            userdata["warnings"].append((warn_type, msg))
+
+        cr.xml_parse_filelists(REPO_01_FILXML, newpkgcb, pkgcb, warningcb)
+
+        self.assertEqual([pkg.name for pkg in userdata["pkgs"]],
+            ['super_kernel'])
+        self.assertEqual(userdata["pkgcb_calls"], 1)
+        self.assertEqual(userdata["warnings"], [])
+
+        pkg = userdata["pkgs"][0]
+        self.assertEqual(pkg.pkgId, "152824bff2aa6d54f429d43e87a3ff3a0286505c6d93ec87692b5e3a9e3b97bf")
+        self.assertEqual(pkg.name, "super_kernel")
+        self.assertEqual(pkg.arch, "x86_64")
+        self.assertEqual(pkg.version, "6.0.1")
+        self.assertEqual(pkg.epoch, "0")
+        self.assertEqual(pkg.release, "2")
+        self.assertEqual(pkg.summary, None)
+        self.assertEqual(pkg.description, None)
+        self.assertEqual(pkg.url, None)
+        self.assertEqual(pkg.time_file, 0)
+        self.assertEqual(pkg.time_build, 0)
+        self.assertEqual(pkg.rpm_license, None)
+        self.assertEqual(pkg.rpm_vendor, None)
+        self.assertEqual(pkg.rpm_group, None)
+        self.assertEqual(pkg.rpm_buildhost, None)
+        self.assertEqual(pkg.rpm_sourcerpm, None)
+        self.assertEqual(pkg.rpm_header_start, 0)
+        self.assertEqual(pkg.rpm_header_end, 0)
+        self.assertEqual(pkg.rpm_packager, None)
+        self.assertEqual(pkg.size_package, 0)
+        self.assertEqual(pkg.size_installed, 0)
+        self.assertEqual(pkg.size_archive, 0)
+        self.assertEqual(pkg.location_href, None)
+        self.assertEqual(pkg.location_base, None)
+        self.assertEqual(pkg.checksum_type, None)
+        self.assertEqual(pkg.requires, [])
+        self.assertEqual(pkg.provides, [])
+        self.assertEqual(pkg.conflicts, [])
+        self.assertEqual(pkg.obsoletes, [])
+        self.assertEqual(pkg.files,
+                [(None, '/usr/bin/', 'super_kernel'),
+                 (None, '/usr/share/man/', 'super_kernel.8.gz')])
+        self.assertEqual(pkg.changelogs, [])
+
+    def test_xml_parser_filelists_repo02(self):
+
+        userdata = {
+                "pkgs": [],
+                "pkgcb_calls": 0,
+                "warnings": []
+            }
+
+        def newpkgcb(pkgId, name, arch):
+            pkg = cr.Package()
+            userdata["pkgs"].append(pkg)
+            return pkg
+
+        def pkgcb():
+            userdata["pkgcb_calls"] += 1
+
+        def warningcb(warn_type, msg):
+            userdata["warnings"].append((warn_type, msg))
+
+        cr.xml_parse_filelists(REPO_02_FILXML, newpkgcb, pkgcb, warningcb)
+
+        self.assertEqual([pkg.name for pkg in userdata["pkgs"]],
+            ['fake_bash', 'super_kernel'])
+        self.assertEqual(userdata["pkgcb_calls"], 2)
+        self.assertEqual(userdata["warnings"], [])
+
+class TestCaseXmlParserOther(unittest.TestCase):
+
+    def test_xml_parser_other_repo01(self):
+
+        userdata = {
+                "pkgs": [],
+                "pkgcb_calls": 0,
+                "warnings": []
+            }
+
+        def newpkgcb(pkgId, name, arch):
+            pkg = cr.Package()
+            userdata["pkgs"].append(pkg)
+            return pkg
+
+        def pkgcb():
+            userdata["pkgcb_calls"] += 1
+
+        def warningcb(warn_type, msg):
+            userdata["warnings"].append((warn_type, msg))
+
+        cr.xml_parse_other(REPO_01_OTHXML, newpkgcb, pkgcb, warningcb)
+
+        self.assertEqual([pkg.name for pkg in userdata["pkgs"]],
+            ['super_kernel'])
+        self.assertEqual(userdata["pkgcb_calls"], 1)
+        self.assertEqual(userdata["warnings"], [])
+
+        pkg = userdata["pkgs"][0]
+        self.assertEqual(pkg.pkgId, "152824bff2aa6d54f429d43e87a3ff3a0286505c6d93ec87692b5e3a9e3b97bf")
+        self.assertEqual(pkg.name, "super_kernel")
+        self.assertEqual(pkg.arch, "x86_64")
+        self.assertEqual(pkg.version, "6.0.1")
+        self.assertEqual(pkg.epoch, "0")
+        self.assertEqual(pkg.release, "2")
+        self.assertEqual(pkg.summary, None)
+        self.assertEqual(pkg.description, None)
+        self.assertEqual(pkg.url, None)
+        self.assertEqual(pkg.time_file, 0)
+        self.assertEqual(pkg.time_build, 0)
+        self.assertEqual(pkg.rpm_license, None)
+        self.assertEqual(pkg.rpm_vendor, None)
+        self.assertEqual(pkg.rpm_group, None)
+        self.assertEqual(pkg.rpm_buildhost, None)
+        self.assertEqual(pkg.rpm_sourcerpm, None)
+        self.assertEqual(pkg.rpm_header_start, 0)
+        self.assertEqual(pkg.rpm_header_end, 0)
+        self.assertEqual(pkg.rpm_packager, None)
+        self.assertEqual(pkg.size_package, 0)
+        self.assertEqual(pkg.size_installed, 0)
+        self.assertEqual(pkg.size_archive, 0)
+        self.assertEqual(pkg.location_href, None)
+        self.assertEqual(pkg.location_base, None)
+        self.assertEqual(pkg.checksum_type, None)
+        self.assertEqual(pkg.requires, [])
+        self.assertEqual(pkg.provides, [])
+        self.assertEqual(pkg.conflicts, [])
+        self.assertEqual(pkg.obsoletes, [])
+        self.assertEqual(pkg.files, [])
+        self.assertEqual(pkg.changelogs,
+                [('Tomas Mlcoch <tmlcoch@redhat.com> - 6.0.1-1',
+                   1334664000L,
+                  '- First release'),
+                 ('Tomas Mlcoch <tmlcoch@redhat.com> - 6.0.1-2',
+                   1334664001L,
+                   '- Second release')])
+
+    def test_xml_parser_other_repo02(self):
+
+        userdata = {
+                "pkgs": [],
+                "pkgcb_calls": 0,
+                "warnings": []
+            }
+
+        def newpkgcb(pkgId, name, arch):
+            pkg = cr.Package()
+            userdata["pkgs"].append(pkg)
+            return pkg
+
+        def pkgcb():
+            userdata["pkgcb_calls"] += 1
+
+        def warningcb(warn_type, msg):
+            userdata["warnings"].append((warn_type, msg))
+
+        cr.xml_parse_other(REPO_02_OTHXML, newpkgcb, pkgcb, warningcb)
+
+        self.assertEqual([pkg.name for pkg in userdata["pkgs"]],
+            ['fake_bash', 'super_kernel'])
+        self.assertEqual(userdata["pkgcb_calls"], 2)
+        self.assertEqual(userdata["warnings"], [])
+
+
+# TODO:
+# Test warnings