From a48db44b73785b5d5fbe8ae827522695fa0fd9ce Mon Sep 17 00:00:00 2001 From: Aleš Matěj Date: Tue, 8 Jan 2019 15:44:55 +0100 Subject: [PATCH] Add support for modular errata (RhBug:1656584) --- src/python/CMakeLists.txt | 1 + src/python/__init__.py | 3 +++ src/python/createrepo_cmodule.c | 8 ++++++++ src/python/updatecollection-py.c | 43 +++++++++++++++++++++++++++++++++++++++++++ src/python/updatecollectionmodule-py.c | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/python/updatecollectionmodule-py.h | 33 +++++++++++++++++++++++++++++++++ src/updateinfo.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/updateinfo.h | 24 ++++++++++++++++++++++++ src/xml_dump_updateinfo.c | 20 ++++++++++++++++++++ src/xml_parser_internal.h | 2 ++ src/xml_parser_updateinfo.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/fixtures.h | 1 + tests/python/tests/test_updatecollection.py | 16 ++++++++++++++++ tests/python/tests/test_updatecollectionmodule.py | 31 +++++++++++++++++++++++++++++++ tests/python/tests/test_updateinfo.py | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_xml_parser_updateinfo.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/testdata/updateinfo_files/updateinfo_03.xml | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 17 files changed, 957 insertions(+) create mode 100644 src/python/updatecollectionmodule-py.c create mode 100644 src/python/updatecollectionmodule-py.h create mode 100644 tests/python/tests/test_updatecollectionmodule.py create mode 100644 tests/testdata/updateinfo_files/updateinfo_03.xml diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt index 9f1ac64..ebf4d4c 100644 --- a/src/python/CMakeLists.txt +++ b/src/python/CMakeLists.txt @@ -50,6 +50,7 @@ SET (createrepo_cmodule_SRCS sqlite-py.c typeconversion.c updatecollection-py.c + updatecollectionmodule-py.c updatecollectionpackage-py.c updateinfo-py.c updaterecord-py.c diff --git a/src/python/__init__.py b/src/python/__init__.py index 6c29e74..65d7f82 100644 --- a/src/python/__init__.py +++ b/src/python/__init__.py @@ -206,6 +206,9 @@ class OtherSqlite(Sqlite): UpdateCollection = _createrepo_c.UpdateCollection +# UpdateCollectionModule class + +UpdateCollectionModule = _createrepo_c.UpdateCollectionModule # UpdateCollectionPackage class diff --git a/src/python/createrepo_cmodule.c b/src/python/createrepo_cmodule.c index fe4d2ad..9be5f46 100644 --- a/src/python/createrepo_cmodule.c +++ b/src/python/createrepo_cmodule.c @@ -35,6 +35,7 @@ #include "repomdrecord-py.h" #include "sqlite-py.h" #include "updatecollection-py.h" +#include "updatecollectionmodule-py.h" #include "updatecollectionpackage-py.h" #include "updateinfo-py.h" #include "updaterecord-py.h" @@ -185,6 +186,13 @@ init_createrepo_c(void) PyModule_AddObject(m, "UpdateCollection", (PyObject *)&UpdateCollection_Type); + /* _createrepo_c.UpdateCollectionModule */ + if (PyType_Ready(&UpdateCollectionModule_Type) < 0) + return FAILURE; + Py_INCREF(&UpdateCollectionModule_Type); + PyModule_AddObject(m, "UpdateCollectionModule", + (PyObject *)&UpdateCollectionModule_Type); + /* _createrepo_c.UpdateCollectionPackage */ if (PyType_Ready(&UpdateCollectionPackage_Type) < 0) return FAILURE; diff --git a/src/python/updatecollection-py.c b/src/python/updatecollection-py.c index 3a791be..ca97657 100644 --- a/src/python/updatecollection-py.c +++ b/src/python/updatecollection-py.c @@ -22,6 +22,7 @@ #include #include "updatecollection-py.h" +#include "updatecollectionmodule-py.h" #include "updatecollectionpackage-py.h" #include "exception-py.h" #include "typeconversion.h" @@ -188,6 +189,13 @@ typedef int (*ConversionToCheckFunc)(PyObject *); typedef void *(*ConversionToFunc)(PyObject *, GStringChunk *); PyObject * +PyObject_FromUpdateCollectionModule(cr_UpdateCollectionModule *module) +{ + return Object_FromUpdateCollectionModule( + cr_updatecollectionmodule_copy(module)); +} + +PyObject * PyObject_FromUpdateCollectionPackage(cr_UpdateCollectionPackage *pkg) { return Object_FromUpdateCollectionPackage( @@ -249,6 +257,23 @@ get_list(_UpdateCollectionObject *self, void *conv) return list; } +static PyObject * +get_module(_UpdateCollectionObject *self, void *member_offset) +{ + if (check_UpdateCollectionStatus(self)) + return NULL; + + cr_UpdateCollection *collection = self->collection; + + cr_UpdateCollectionModule *module = *((cr_UpdateCollectionModule **) ((size_t) collection + (size_t) member_offset)); + if (module == NULL) + Py_RETURN_NONE; + + PyObject *py_module = PyObject_FromUpdateCollectionModule(module); + + return py_module; +} + static int set_str(_UpdateCollectionObject *self, PyObject *value, void *member_offset) { @@ -265,11 +290,29 @@ set_str(_UpdateCollectionObject *self, PyObject *value, void *member_offset) return 0; } +static int +set_module(_UpdateCollectionObject *self, PyObject *value, void *member_offset) +{ + if (check_UpdateCollectionStatus(self)) + return -1; + if (!UpdateCollectionModuleObject_Check(value) && value != Py_None) { + PyErr_SetString(PyExc_TypeError, "Module or None expected!"); + return -1; + } + cr_UpdateCollectionModule *module = UpdateCollectionModule_FromPyObject(value); + cr_UpdateCollection *collection = self->collection; + *((cr_UpdateCollectionModule **) ((size_t) collection + (size_t) member_offset)) = module; + + return 0; +} + static PyGetSetDef updatecollection_getsetters[] = { {"shortname", (getter)get_str, (setter)set_str, "Short name", OFFSET(shortname)}, {"name", (getter)get_str, (setter)set_str, "Name of the collection", OFFSET(name)}, + {"module", (getter)get_module, (setter)set_module, + "Module information", OFFSET(module)}, {"packages", (getter)get_list, (setter)NULL, "List of packages", &(list_convertors[0])}, {NULL, NULL, NULL, NULL, NULL} /* sentinel */ diff --git a/src/python/updatecollectionmodule-py.c b/src/python/updatecollectionmodule-py.c new file mode 100644 index 0000000..20b2d99 --- /dev/null +++ b/src/python/updatecollectionmodule-py.c @@ -0,0 +1,274 @@ +/* createrepo_c - Library of routines for manipulation with repodata + * Copyright (C) 2013 Tomas Mlcoch + * + * 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. + */ + +#include +#include +#include + +#include "updatecollectionmodule-py.h" +#include "exception-py.h" +#include "typeconversion.h" +#include "contentstat-py.h" + +typedef struct { + PyObject_HEAD + cr_UpdateCollectionModule *module; +} _UpdateCollectionModuleObject; + +PyObject * +Object_FromUpdateCollectionModule(cr_UpdateCollectionModule *mod) +{ + PyObject *py_rec; + + if (!mod) { + PyErr_SetString(PyExc_ValueError, "Expected a cr_UpdateCollectionModule pointer not NULL."); + return NULL; + } + + py_rec = PyObject_CallObject((PyObject *) &UpdateCollectionModule_Type, NULL); + cr_updatecollectionmodule_free(((_UpdateCollectionModuleObject *)py_rec)->module); + ((_UpdateCollectionModuleObject *)py_rec)->module = mod; + + return py_rec; +} + +cr_UpdateCollectionModule * +UpdateCollectionModule_FromPyObject(PyObject *o) +{ + if (!UpdateCollectionModuleObject_Check(o)) { + PyErr_SetString(PyExc_TypeError, "Expected a UpdateCollectionModule object."); + return NULL; + } + return ((_UpdateCollectionModuleObject *)o)->module; +} + +static int +check_UpdateCollectionModuleStatus(const _UpdateCollectionModuleObject *self) +{ + assert(self != NULL); + assert(UpdateCollectionModuleObject_Check(self)); + if (self->module == NULL) { + PyErr_SetString(CrErr_Exception, "Improper createrepo_c UpdateCollectionModule object."); + return -1; + } + return 0; +} + +/* Function on the type */ + +static PyObject * +updatecollectionmodule_new(PyTypeObject *type, + G_GNUC_UNUSED PyObject *args, + G_GNUC_UNUSED PyObject *kwds) +{ + _UpdateCollectionModuleObject *self = (_UpdateCollectionModuleObject *)type->tp_alloc(type, 0); + if (self) { + self->module = NULL; + } + return (PyObject *)self; +} + +PyDoc_STRVAR(updatecollectionmodule_init__doc__, +".. method:: __init__()\n\n"); + +static int +updatecollectionmodule_init(_UpdateCollectionModuleObject *self, + G_GNUC_UNUSED PyObject *args, + G_GNUC_UNUSED PyObject *kwds) +{ + /* Free all previous resources when reinitialization */ + if (self->module) + cr_updatecollectionmodule_free(self->module); + + /* Init */ + self->module = cr_updatecollectionmodule_new(); + if (self->module == NULL) { + PyErr_SetString(CrErr_Exception, "UpdateCollectionModule initialization failed"); + return -1; + } + + return 0; +} + +static void +updatecollectionmodule_dealloc(_UpdateCollectionModuleObject *self) +{ + if (self->module) + cr_updatecollectionmodule_free(self->module); + Py_TYPE(self)->tp_free(self); +} + +static PyObject * +updatecollectionmodule_repr(G_GNUC_UNUSED _UpdateCollectionModuleObject *self) +{ + return PyUnicode_FromFormat(""); +} + +/* UpdateCollectionModule methods */ + +PyDoc_STRVAR(copy__doc__, +"copy() -> UpdateCollectionModule\n\n" +"Return copy of the UpdateCollectionModule object"); + +static PyObject * +copy_updatecollectionmodule(_UpdateCollectionModuleObject *self, + G_GNUC_UNUSED void *nothing) +{ + if (check_UpdateCollectionModuleStatus(self)) + return NULL; + return Object_FromUpdateCollectionModule(cr_updatecollectionmodule_copy(self->module)); +} + +static struct PyMethodDef updatecollectionmodule_methods[] = { + {"copy", (PyCFunction)copy_updatecollectionmodule, METH_NOARGS, + copy__doc__}, + {NULL} /* sentinel */ +}; + +/* getsetters */ + +#define OFFSET(member) (void *) offsetof(cr_UpdateCollectionModule, member) + +static PyObject * +get_str(_UpdateCollectionModuleObject *self, void *member_offset) +{ + if (check_UpdateCollectionModuleStatus(self)) + return NULL; + cr_UpdateCollectionModule *module = self->module; + char *str = *((char **) ((size_t) module + (size_t) member_offset)); + if (str == NULL) + Py_RETURN_NONE; + return PyUnicode_FromString(str); +} + +static PyObject * +get_uint(_UpdateCollectionModuleObject *self, void *member_offset) +{ + if (check_UpdateCollectionModuleStatus(self)) + return NULL; + cr_UpdateCollectionModule *module = self->module; + guint64 val = *((guint64 *) ((size_t) module + (size_t) member_offset)); + return PyLong_FromUnsignedLongLong((guint64) val); +} + +static int +set_str(_UpdateCollectionModuleObject *self, PyObject *value, void *member_offset) +{ + if (check_UpdateCollectionModuleStatus(self)) + return -1; + if (!PyUnicode_Check(value) && !PyBytes_Check(value) && value != Py_None) { + PyErr_SetString(PyExc_TypeError, "Unicode, bytes, or None expected!"); + return -1; + } + + if (PyUnicode_Check(value)) { + value = PyUnicode_AsUTF8String(value); + } + + cr_UpdateCollectionModule *module = self->module; + char *str = cr_safe_string_chunk_insert(module->chunk, + PyObject_ToStrOrNull(value)); + + *((char **) ((size_t) module + (size_t) member_offset)) = str; + return 0; +} + +static int +set_uint(_UpdateCollectionModuleObject *self, PyObject *value, void *member_offset) +{ + if (check_UpdateCollectionModuleStatus(self)) + return -1; + guint64 val; + + if (PyLong_Check(value)) { + val = PyLong_AsUnsignedLongLong(value); + } else if (PyFloat_Check(value)) { + val = (guint64) PyFloat_AS_DOUBLE(value); +#if PY_MAJOR_VERSION < 3 + } else if (PyInt_Check(value)) { + val = PyInt_AS_LONG(value); +#endif + } else { + PyErr_SetString(PyExc_TypeError, "Number expected!"); + return -1; + } + + cr_UpdateCollectionModule *module = self->module; + *((guint64 *) ((size_t) module + (size_t) member_offset)) = (guint64) val; + return 0; +} + +static PyGetSetDef updatecollectionmodule_getsetters[] = { + {"name", (getter)get_str, (setter)set_str, + "Name", OFFSET(name)}, + {"stream", (getter)get_str, (setter)set_str, + "Stream", OFFSET(stream)}, + {"version", (getter)get_uint, (setter)set_uint, + "Version", OFFSET(version)}, + {"context", (getter)get_str, (setter)set_str, + "Context", OFFSET(context)}, + {"arch", (getter)get_str, (setter)set_str, + "Arch", OFFSET(arch)}, + {NULL, NULL, NULL, NULL, NULL} /* sentinel */ +}; + +/* Object */ + +PyTypeObject UpdateCollectionModule_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "createrepo_c.UpdateCollectionModule", /* tp_name */ + sizeof(_UpdateCollectionModuleObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) updatecollectionmodule_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc) updatecollectionmodule_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 */ + updatecollectionmodule_init__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + 0, /* tp_iternext */ + updatecollectionmodule_methods, /* tp_methods */ + 0, /* tp_members */ + updatecollectionmodule_getsetters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc) updatecollectionmodule_init,/* tp_init */ + 0, /* tp_alloc */ + updatecollectionmodule_new, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ +}; diff --git a/src/python/updatecollectionmodule-py.h b/src/python/updatecollectionmodule-py.h new file mode 100644 index 0000000..5847259 --- /dev/null +++ b/src/python/updatecollectionmodule-py.h @@ -0,0 +1,33 @@ +/* createrepo_c - Library of routines for manipulation with repodata + * Copyright (C) 2013 Tomas Mlcoch + * + * 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. + */ + +#ifndef CR_UPDATECOLLECTIONMODULE_PY_H +#define CR_UPDATECOLLECTIONMODULE_PY_H + +#include "src/createrepo_c.h" + +extern PyTypeObject UpdateCollectionModule_Type; + +#define UpdateCollectionModuleObject_Check(o) \ + PyObject_TypeCheck(o, &UpdateCollectionModule_Type) + +PyObject *Object_FromUpdateCollectionModule(cr_UpdateCollectionModule *rec); +cr_UpdateCollectionModule *UpdateCollectionModule_FromPyObject(PyObject *o); + +#endif diff --git a/src/updateinfo.c b/src/updateinfo.c index 6e45229..cdc4747 100644 --- a/src/updateinfo.c +++ b/src/updateinfo.c @@ -74,6 +74,46 @@ cr_updatecollectionpackage_free(cr_UpdateCollectionPackage *pkg) /* + * cr_UpdateCollectionModule + */ + +cr_UpdateCollectionModule * +cr_updatecollectionmodule_new(void) +{ + cr_UpdateCollectionModule *module = g_malloc0(sizeof(*module)); + module->chunk = g_string_chunk_new(0); + return module; +} + +cr_UpdateCollectionModule * +cr_updatecollectionmodule_copy(const cr_UpdateCollectionModule *orig) +{ + cr_UpdateCollectionModule *module; + + if (!orig) return NULL; + + module = cr_updatecollectionmodule_new(); + + module->name = cr_safe_string_chunk_insert(module->chunk, orig->name); + module->stream = cr_safe_string_chunk_insert(module->chunk, orig->stream); + module->version = orig->version; + module->context = cr_safe_string_chunk_insert(module->chunk, orig->context); + module->arch = cr_safe_string_chunk_insert(module->chunk, orig->arch); + + return module; +} + +void +cr_updatecollectionmodule_free(cr_UpdateCollectionModule *module) +{ + if (!module) + return; + g_string_chunk_free(module->chunk); + g_free(module); +} + + +/* * cr_UpdateCollection */ @@ -97,6 +137,10 @@ cr_updatecollection_copy(const cr_UpdateCollection *orig) col->shortname = cr_safe_string_chunk_insert(col->chunk, orig->shortname); col->name = cr_safe_string_chunk_insert(col->chunk, orig->name); + if (orig->module) { + col->module = cr_updatecollectionmodule_copy(orig->module); + } + if (orig->packages) { GSList *newlist = NULL; for (GSList *elem = orig->packages; elem; elem = g_slist_next(elem)) { diff --git a/src/updateinfo.h b/src/updateinfo.h index dbf7807..38883e0 100644 --- a/src/updateinfo.h +++ b/src/updateinfo.h @@ -51,8 +51,19 @@ typedef struct { } cr_UpdateCollectionPackage; typedef struct { + gchar *name; + gchar *stream; + guint64 version; + gchar *context; + gchar *arch; + + GStringChunk *chunk; +} cr_UpdateCollectionModule; + +typedef struct { gchar *shortname; /*!< e.g. rhn-tools-rhel-x86_64-server-6.5.aus */ gchar *name; /*!< e.g. RHN Tools for RHEL AUS (v. 6.5 for 64-bit x86_64) */ + cr_UpdateCollectionModule *module; GSList *packages; /*!< List of cr_UpdateCollectionPackage */ GStringChunk *chunk; } cr_UpdateCollection; @@ -106,6 +117,19 @@ void cr_updatecollectionpackage_free(cr_UpdateCollectionPackage *pkg); /* + * cr_UpdateCollectionModule + */ + +cr_UpdateCollectionModule * +cr_updatecollectionmodule_new(void); + +cr_UpdateCollectionModule * +cr_updatecollectionmodule_copy(const cr_UpdateCollectionModule *orig); + +void +cr_updatecollectionmodule_free(cr_UpdateCollectionModule *pkg); + +/* * cr_UpdateCollection */ diff --git a/src/xml_dump_updateinfo.c b/src/xml_dump_updateinfo.c index 4fb5720..fafe686 100644 --- a/src/xml_dump_updateinfo.c +++ b/src/xml_dump_updateinfo.c @@ -66,6 +66,24 @@ cr_xml_dump_updatecollectionpackages(xmlNodePtr collection, GSList *packages) } void +cr_xml_dump_updatecollectionmodule(xmlNodePtr collection, cr_UpdateCollectionModule *module) +{ + if (!module) + return; + + xmlNodePtr xml_module; + xml_module = xmlNewChild(collection, NULL, BAD_CAST "module", NULL); + + cr_xmlNewProp_c(xml_module, BAD_CAST "name", BAD_CAST module->name); + cr_xmlNewProp_c(xml_module, BAD_CAST "stream", BAD_CAST module->stream); + gchar buf[21]; //20 + '\0' is max number of chars of guint64: G_MAXUINT64 (= 18,446,744,073,709,551,615) + snprintf(buf, 21, "%" G_GUINT64_FORMAT, module->version); + cr_xmlNewProp_c(xml_module, BAD_CAST "version", BAD_CAST buf); + cr_xmlNewProp_c(xml_module, BAD_CAST "context", BAD_CAST module->context); + cr_xmlNewProp_c(xml_module, BAD_CAST "arch", BAD_CAST module->arch); +} + +void cr_xml_dump_updateinforecord_pkglist(xmlNodePtr update, GSList *collections) { xmlNodePtr pkglist; @@ -83,6 +101,8 @@ cr_xml_dump_updateinforecord_pkglist(xmlNodePtr update, GSList *collections) BAD_CAST "name", BAD_CAST col->name); + cr_xml_dump_updatecollectionmodule(collection, col->module); + cr_xml_dump_updatecollectionpackages(collection, col->packages); } } diff --git a/src/xml_parser_internal.h b/src/xml_parser_internal.h index 6b400eb..e079ece 100644 --- a/src/xml_parser_internal.h +++ b/src/xml_parser_internal.h @@ -151,6 +151,8 @@ typedef struct _cr_ParserData { Update record object */ cr_UpdateCollection *updatecollection; /*!< Update collection object */ + cr_UpdateCollectionModule *updatecollectionmodule; /*!< + Update collection module object */ cr_UpdateCollectionPackage *updatecollectionpackage; /*!< Update collection package object */ diff --git a/src/xml_parser_updateinfo.c b/src/xml_parser_updateinfo.c index 18e5277..c6c6503 100644 --- a/src/xml_parser_updateinfo.c +++ b/src/xml_parser_updateinfo.c @@ -54,6 +54,7 @@ typedef enum { STATE_PKGLIST, // ---------------------------- STATE_COLLECTION, STATE_NAME, + STATE_MODULE, STATE_PACKAGE, STATE_FILENAME, STATE_SUM, @@ -89,6 +90,7 @@ static cr_StatesSwitch stateswitches[] = { { STATE_PKGLIST, "collection", STATE_COLLECTION, 0 }, { STATE_COLLECTION, "package", STATE_PACKAGE, 0 }, { STATE_COLLECTION, "name", STATE_NAME, 1 }, + { STATE_COLLECTION, "module", STATE_MODULE, 0 }, { STATE_PACKAGE, "filename", STATE_FILENAME, 1 }, { STATE_PACKAGE, "sum", STATE_SUM, 1 }, { STATE_PACKAGE, "reboot_suggested", STATE_REBOOTSUGGESTED, 0 }, @@ -141,6 +143,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr) // Shortcuts cr_UpdateRecord *rec = pd->updaterecord; cr_UpdateCollection *collection = pd->updatecollection; + cr_UpdateCollectionModule *module = pd->updatecollectionmodule; cr_UpdateCollectionPackage *package = pd->updatecollectionpackage; switch(pd->state) { @@ -173,6 +176,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr) assert(pd->updateinfo); assert(!pd->updaterecord); assert(!pd->updatecollection); + assert(!pd->updatecollectionmodule); assert(!pd->updatecollectionpackage); rec = cr_updaterecord_new(); @@ -201,6 +205,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr) assert(pd->updateinfo); assert(pd->updaterecord); assert(!pd->updatecollection); + assert(!pd->updatecollectionmodule); assert(!pd->updatecollectionpackage); val = cr_find_attr("date", attr); if (val) @@ -211,6 +216,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr) assert(pd->updateinfo); assert(pd->updaterecord); assert(!pd->updatecollection); + assert(!pd->updatecollectionmodule); assert(!pd->updatecollectionpackage); val = cr_find_attr("date", attr); if (val) @@ -223,6 +229,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr) assert(pd->updateinfo); assert(pd->updaterecord); assert(!pd->updatecollection); + assert(!pd->updatecollectionmodule); assert(!pd->updatecollectionpackage); ref = cr_updatereference_new(); @@ -251,6 +258,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr) assert(pd->updateinfo); assert(pd->updaterecord); assert(!pd->updatecollection); + assert(!pd->updatecollectionmodule); assert(!pd->updatecollectionpackage); collection = cr_updatecollection_new(); @@ -263,6 +271,49 @@ cr_start_handler(void *pdata, const char *element, const char **attr) break; + case STATE_MODULE: + assert(pd->updateinfo); + assert(pd->updaterecord); + assert(pd->updatecollection); + assert(!pd->updatecollectionmodule); + assert(!pd->updatecollectionpackage); + + module = cr_updatecollectionmodule_new(); + if (module) + collection->module = module; + + val = cr_find_attr("name", attr); + if (val) + module->name = g_string_chunk_insert(module->chunk, val); + + val = cr_find_attr("stream", attr); + if (val) + module->stream = g_string_chunk_insert(module->chunk, val); + + val = cr_find_attr("version", attr); + if (val){ + gchar *endptr; + errno = 0; + module->version = strtoull(val, &endptr, 10); + if ((errno == ERANGE && (module->version == ULLONG_MAX)) + || (errno != 0 && module->version == 0)) { + perror("strtoull error when parsing module version"); + module->version = 0; + } + if (endptr == val) + module->version = 0; + } + + val = cr_find_attr("context", attr); + if (val) + module->context = g_string_chunk_insert(module->chunk, val); + + val = cr_find_attr("arch", attr); + if (val) + module->arch = g_string_chunk_insert(module->chunk, val); + + break; + case STATE_PACKAGE: assert(pd->updateinfo); assert(pd->updaterecord); @@ -303,6 +354,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr) assert(pd->updateinfo); assert(pd->updaterecord); assert(pd->updatecollection); + assert(pd->updatecollectionmodule); assert(pd->updatecollectionpackage); val = cr_find_attr("type", attr); if (val) @@ -313,6 +365,7 @@ cr_start_handler(void *pdata, const char *element, const char **attr) assert(pd->updateinfo); assert(pd->updaterecord); assert(pd->updatecollection); + assert(pd->updatecollectionmodule); assert(pd->updatecollectionpackage); package->reboot_suggested = TRUE; break; @@ -352,6 +405,7 @@ cr_end_handler(void *pdata, G_GNUC_UNUSED const char *element) case STATE_UPDATED: case STATE_REFERENCES: case STATE_REFERENCE: + case STATE_MODULE: case STATE_PKGLIST: case STATE_REBOOTSUGGESTED: // All elements with no text data and without need of any diff --git a/tests/fixtures.h b/tests/fixtures.h index ee374f5..8567714 100644 --- a/tests/fixtures.h +++ b/tests/fixtures.h @@ -83,5 +83,6 @@ #define TEST_UPDATEINFO_00 TEST_UPDATEINFO_FILES_PATH"updateinfo_00.xml" #define TEST_UPDATEINFO_01 TEST_UPDATEINFO_FILES_PATH"updateinfo_01.xml" #define TEST_UPDATEINFO_02 TEST_UPDATEINFO_FILES_PATH"updateinfo_02.xml.xz" +#define TEST_UPDATEINFO_03 TEST_UPDATEINFO_FILES_PATH"updateinfo_03.xml" #endif diff --git a/tests/python/tests/test_updatecollection.py b/tests/python/tests/test_updatecollection.py index f3433c0..71ac7dd 100644 --- a/tests/python/tests/test_updatecollection.py +++ b/tests/python/tests/test_updatecollection.py @@ -16,6 +16,13 @@ class TestCaseUpdateCollection(unittest.TestCase): self.assertEqual(col.name, None) self.assertEqual(col.packages, []) + module = cr.UpdateCollectionModule() + module.name = "kangaroo" + module.stream = "0" + module.version = 20180730223407 + module.context = "deadbeef" + module.arch = "noarch" + pkg = cr.UpdateCollectionPackage() pkg.name = "foo" pkg.version = "1.2" @@ -30,12 +37,21 @@ class TestCaseUpdateCollection(unittest.TestCase): col.shortname = "short name" col.name = "long name" + col.module = module col.append(pkg) self.assertEqual(col.shortname, "short name") self.assertEqual(col.name, "long name") self.assertEqual(len(col.packages), 1) + # Check if the appended module was appended properly + module = col.module + self.assertEqual(module.name, "kangaroo") + self.assertEqual(module.stream, "0") + self.assertEqual(module.version, 20180730223407) + self.assertEqual(module.context, "deadbeef") + self.assertEqual(module.arch, "noarch") + # Also check if the appended package was appended properly pkg = col.packages[0] self.assertEqual(pkg.name, "foo") diff --git a/tests/python/tests/test_updatecollectionmodule.py b/tests/python/tests/test_updatecollectionmodule.py new file mode 100644 index 0000000..1e92b12 --- /dev/null +++ b/tests/python/tests/test_updatecollectionmodule.py @@ -0,0 +1,31 @@ +import unittest +import shutil +import tempfile +import os.path +import createrepo_c as cr + +from .fixtures import * + +class TestCaseUpdateCollectionModule(unittest.TestCase): + + def test_updatecollectionmodule_setters(self): + module = cr.UpdateCollectionModule() + self.assertTrue(module) + + self.assertEqual(module.name, None) + self.assertEqual(module.stream, None) + self.assertEqual(module.version, 0) + self.assertEqual(module.context, None) + self.assertEqual(module.arch, None) + + module.name = "foo" + module.stream = "0" + module.version = 20180730223407 + module.context = "deadbeef" + module.arch = "noarch" + + self.assertEqual(module.name, "foo") + self.assertEqual(module.stream, "0") + self.assertEqual(module.version, 20180730223407) + self.assertEqual(module.context, "deadbeef") + self.assertEqual(module.arch, "noarch") diff --git a/tests/python/tests/test_updateinfo.py b/tests/python/tests/test_updateinfo.py index f3b88e1..727b707 100644 --- a/tests/python/tests/test_updateinfo.py +++ b/tests/python/tests/test_updateinfo.py @@ -123,6 +123,100 @@ class TestCaseUpdateInfo(unittest.TestCase): now = datetime(now.year, now.month, now.day, now.hour, now.minute, now.second, 0) + mod = cr.UpdateCollectionModule() + mod.name = "kangaroo" + mod.stream = "0" + mod.version = 18446744073709551615 + mod.context = "deadbeef" + mod.arch = "x86" + + pkg = cr.UpdateCollectionPackage() + pkg.name = "foo" + pkg.version = "1.2" + pkg.release = "3" + pkg.epoch = "0" + pkg.arch = "x86" + pkg.src = "foo.src.rpm" + pkg.filename = "foo.rpm" + pkg.sum = "abcdef" + pkg.sum_type = cr.SHA1 + pkg.reboot_suggested = True + + col = cr.UpdateCollection() + col.shortname = "short name" + col.name = "long name" + col.module = mod + col.append(pkg) + + ref = cr.UpdateReference() + ref.href = "href" + ref.id = "id" + ref.type = "type" + ref.title = "title" + + rec = cr.UpdateRecord() + rec.fromstr = "from" + rec.status = "status" + rec.type = "type" + rec.version = "version" + rec.id = "id" + rec.title = "title" + rec.issued_date = now + rec.updated_date = now + rec.rights = "rights" + rec.release = "release" + rec.pushcount = "pushcount" + rec.severity = "severity" + rec.summary = "summary" + rec.description = "description" + rec.solution = "solution" + rec.append_collection(col) + rec.append_reference(ref) + + ui = cr.UpdateInfo() + ui.append(rec) + + xml = ui.xml_dump() + + self.assertEqual(xml, +""" + + + id + title + + + rights + release + pushcount + severity + summary + description + solution + + + + + + long name + + + foo.rpm + abcdef + + + + + + +""" % {"now": now.strftime("%Y-%m-%d %H:%M:%S")}) + + def test_updateinfo_xml_dump_04(self): + now = datetime.now() + # Microseconds are always 0 in updateinfo + now = datetime(now.year, now.month, now.day, now.hour, now.minute, + now.second, 0) + pkg = cr.UpdateCollectionPackage() pkg.name = "foo" pkg.version = "1.2" @@ -135,6 +229,7 @@ class TestCaseUpdateInfo(unittest.TestCase): pkg.sum_type = cr.SHA1 pkg.reboot_suggested = True + # Collection without module col = cr.UpdateCollection() col.shortname = "short name" col.name = "long name" @@ -167,6 +262,99 @@ class TestCaseUpdateInfo(unittest.TestCase): ui = cr.UpdateInfo() ui.append(rec) + + xml = ui.xml_dump() + + self.assertEqual(xml, +""" + + + id + title + + + rights + release + pushcount + severity + summary + description + solution + + + + + + long name + + foo.rpm + abcdef + + + + + + +""" % {"now": now.strftime("%Y-%m-%d %H:%M:%S")}) + + def test_updateinfo_xml_dump_05(self): + now = datetime.now() + # Microseconds are always 0 in updateinfo + now = datetime(now.year, now.month, now.day, now.hour, now.minute, + now.second, 0) + + # Collection module with unset fields + mod = cr.UpdateCollectionModule() + mod.version = 18446744073709551615 + mod.context = "deadbeef" + mod.arch = "x86" + + pkg = cr.UpdateCollectionPackage() + pkg.name = "foo" + pkg.version = "1.2" + pkg.release = "3" + pkg.epoch = "0" + pkg.arch = "x86" + pkg.src = "foo.src.rpm" + pkg.filename = "foo.rpm" + pkg.sum = "abcdef" + pkg.sum_type = cr.SHA1 + pkg.reboot_suggested = True + + col = cr.UpdateCollection() + col.shortname = "short name" + col.name = "long name" + col.module = mod + col.append(pkg) + + ref = cr.UpdateReference() + ref.href = "href" + ref.id = "id" + ref.type = "type" + ref.title = "title" + + rec = cr.UpdateRecord() + rec.fromstr = "from" + rec.status = "status" + rec.type = "type" + rec.version = "version" + rec.id = "id" + rec.title = "title" + rec.issued_date = now + rec.updated_date = now + rec.rights = "rights" + rec.release = "release" + rec.pushcount = "pushcount" + rec.severity = "severity" + rec.summary = "summary" + rec.description = "description" + rec.solution = "solution" + rec.append_collection(col) + rec.append_reference(ref) + + ui = cr.UpdateInfo() + ui.append(rec) + xml = ui.xml_dump() self.assertEqual(xml, @@ -190,6 +378,7 @@ class TestCaseUpdateInfo(unittest.TestCase): long name + foo.rpm abcdef diff --git a/tests/test_xml_parser_updateinfo.c b/tests/test_xml_parser_updateinfo.c index 94768ce..3f0cfee 100644 --- a/tests/test_xml_parser_updateinfo.c +++ b/tests/test_xml_parser_updateinfo.c @@ -168,6 +168,90 @@ test_cr_xml_parse_updateinfo_02(void) cr_updateinfo_free(ui); } +//Test for module support +static void +test_cr_xml_parse_updateinfo_03(void) +{ + GError *tmp_err = NULL; + cr_UpdateInfo *ui = cr_updateinfo_new(); + cr_UpdateRecord *update; + cr_UpdateReference *ref; + cr_UpdateCollection *col; + cr_UpdateCollectionModule *module; + cr_UpdateCollectionPackage *pkg; + + int ret = cr_xml_parse_updateinfo(TEST_UPDATEINFO_03, ui, + NULL, NULL, &tmp_err); + + g_assert(tmp_err == NULL); + g_assert_cmpint(ret, ==, CRE_OK); + + g_assert_cmpint(g_slist_length(ui->updates), ==, 6); + update = g_slist_nth_data(ui->updates, 3); + + g_assert_cmpstr(update->from, ==, "errata@redhat.com"); + g_assert_cmpstr(update->status, ==, "stable"); + g_assert_cmpstr(update->type, ==, "enhancement"); + g_assert_cmpstr(update->version, ==, "1"); + g_assert_cmpstr(update->id, ==, "RHEA-2012:0058"); + g_assert_cmpstr(update->title, ==, "Gorilla_Erratum"); + g_assert_cmpstr(update->description, ==, "Gorilla_Erratum"); + + update = g_slist_nth_data(ui->updates, 4); + + g_assert_cmpstr(update->id, ==, "RHEA-2012:0059"); + g_assert_cmpstr(update->title, ==, "Duck_Kangaroo_Erratum"); + g_assert_cmpstr(update->description, ==, "Duck_Kangaro_Erratum description"); + g_assert_cmpstr(update->issued_date, ==, "2018-01-27 16:08:09"); + g_assert_cmpstr(update->updated_date, ==, "2018-07-20 06:00:01 UTC"); + g_assert_cmpstr(update->release, ==, "1"); + + g_assert_cmpint(g_slist_length(update->references), ==, 0); + + g_assert_cmpint(g_slist_length(update->collections), ==, 2); + col = g_slist_nth_data(update->collections, 0); + g_assert_cmpstr(col->shortname, ==, ""); + g_assert_cmpstr(col->name, ==, "coll_name1"); + + module = col->module; + g_assert_cmpstr(module->name, ==, "kangaroo"); + g_assert_cmpstr(module->stream, ==, "0"); + g_assert_cmpuint(module->version, ==, 20180730223407); + g_assert_cmpstr(module->context, ==, "deadbeef"); + g_assert_cmpstr(module->arch, ==, "noarch"); + + g_assert_cmpint(g_slist_length(col->packages), ==, 1); + pkg = col->packages->data; + g_assert_cmpstr(pkg->name, ==, "kangaroo"); + g_assert_cmpstr(pkg->version, ==, "0.3"); + g_assert_cmpstr(pkg->release, ==, "1"); + g_assert(!pkg->epoch); + g_assert_cmpstr(pkg->arch, ==, "noarch"); + g_assert_cmpstr(pkg->src, ==, "http://www.fedoraproject.org"); + g_assert_cmpstr(pkg->filename, ==, "kangaroo-0.3-1.noarch.rpm"); + g_assert(!pkg->sum); + g_assert(!pkg->sum_type); + + col = g_slist_nth_data(update->collections, 1); + g_assert_cmpstr(col->shortname, ==, ""); + g_assert_cmpstr(col->name, ==, "coll_name2"); + + module = col->module; + g_assert_cmpstr(module->name, ==, "duck"); + g_assert_cmpstr(module->stream, ==, "0"); + g_assert_cmpuint(module->version, ==, 20180730233102); + g_assert_cmpstr(module->context, ==, "deadbeef"); + g_assert_cmpstr(module->arch, ==, "noarch"); + + g_assert_cmpint(g_slist_length(col->packages), ==, 1); + pkg = col->packages->data; + g_assert_cmpstr(pkg->name, ==, "duck"); + g_assert_cmpstr(pkg->version, ==, "0.7"); + g_assert_cmpstr(pkg->filename, ==, "duck-0.7-1.noarch.rpm"); + + cr_updateinfo_free(ui); +} + int main(int argc, char *argv[]) { @@ -179,6 +263,8 @@ main(int argc, char *argv[]) test_cr_xml_parse_updateinfo_01); g_test_add_func("/xml_parser_updateinfo/test_cr_xml_parse_updateinfo_02", test_cr_xml_parse_updateinfo_02); + g_test_add_func("/xml_parser_updateinfo/test_cr_xml_parse_updateinfo_03", + test_cr_xml_parse_updateinfo_03); return g_test_run(); } diff --git a/tests/testdata/updateinfo_files/updateinfo_03.xml b/tests/testdata/updateinfo_files/updateinfo_03.xml new file mode 100644 index 0000000..ddbd99b --- /dev/null +++ b/tests/testdata/updateinfo_files/updateinfo_03.xml @@ -0,0 +1,128 @@ + + + + RHEA-2012:0055 + Sea_Erratum + 1 + + + Sea_Erratum + + + 1 + + walrus-5.21-1.noarch.rpm + + + penguin-0.9.1-1.noarch.rpm + + + shark-0.1-1.noarch.rpm + + + + + + + RHEA-2012:0056 + Bird_Erratum + 1 + + + ParthaBird_Erratum + + + 1 + + crow-0.8-1.noarch.rpm + + + stork-0.12-2.noarch.rpm + + + duck-0.6-1.noarch.rpm + + + + + + + RHEA-2012:0057 + Bear_ErratumPARTHA + 1 + + + Bear_Erratum + + + 1 + + bear-4.1-1.noarch.rpm + + + + + + + RHEA-2012:0058 + Gorilla_Erratum + 1 + + + Gorilla_Erratum + + + 1 + + gorilla-0.62-1.noarch.rpm + + + + + + + RHEA-2012:0059 + Duck_Kangaroo_Erratum + 1 + + + Duck_Kangaro_Erratum description + + + coll_name1 + + + kangaroo-0.3-1.noarch.rpm + + + + coll_name2 + + + duck-0.7-1.noarch.rpm + + + + + + + RHEA-2012:0060 + Duck_0.8_Erratum + 1 + + + Duck_0.8_Erratum description + + + coll_name + + + duck-0.8-1.noarch.rpm + + + + + -- libgit2 0.27.8