Further extend callbacks to cover ResolvableRemove
authorKlaus Kämpf <kkaempf@suse.de>
Fri, 12 Aug 2011 14:57:13 +0000 (16:57 +0200)
committerKlaus Kämpf <kkaempf@suse.de>
Fri, 12 Aug 2011 14:57:13 +0000 (16:57 +0200)
Still incomplete due to missing type conversion for
target::rpm::RemoveResolvableReport::Error

swig/Callbacks.i
swig/CommitCallbacks.h
swig/python/tests/commit_callbacks.py
swig/ruby/tests/commit_callbacks.rb

index d4186aa..0aecb20 100644 (file)
 
 %{
 #include <cstdarg>
+#include <zypp/ZYppCallbacks.h>
+/*
+ * Helpers
+ *
+ */
+/*
+ * Action
+ * Symbol representation of :abort, :retry, and :ignore
+ *
+ */
+
+static Target_Type action_abort()
+  {
+#if defined(SWIGRUBY)
+    static VALUE value = Qnil;
+    if (value == Qnil)
+      value = ID2SYM(rb_intern("abort"));
+    return value;
+#endif
+  }
 
+static Target_Type action_retry()
+  {
+#if defined(SWIGRUBY)
+    static VALUE value = Qnil;
+    if (value == Qnil)
+      value = ID2SYM(rb_intern("retry"));
+    return value;
+#endif
+  }
+
+static Target_Type action_ignore()
+  {
+#if defined(SWIGRUBY)
+    static VALUE value = Qnil;
+    if (value == Qnil)
+      value = ID2SYM(rb_intern("ignore"));
+    return value;
+#endif
+  }
+
+/*
+ * Error
+ * Symbol representation of :no_error, :not_found, :io, :invalid
+ *
+ */
+
+static Target_Type error_no_error()
+  {
+#if defined(SWIGRUBY)
+    static VALUE value = Qnil;
+    if (value == Qnil)
+      value = ID2SYM(rb_intern("no_error"));
+    return value;
+#endif
+#if defined(SWIGPYTHON)
+  return Target_String("no_error");
+#endif
+  }
+
+static Target_Type error_not_found()
+  {
+#if defined(SWIGRUBY)
+    static VALUE value = Qnil;
+    if (value == Qnil)
+      value = ID2SYM(rb_intern("not_found"));
+    return value;
+#endif
+#if defined(SWIGPYTHON)
+  return Target_String("not_found");
+#endif
+  }
+
+static Target_Type error_io()
+  {
+#if defined(SWIGRUBY)
+    static VALUE value = Qnil;
+    if (value == Qnil)
+      value = ID2SYM(rb_intern("io"));
+    return value;
+#endif
+#if defined(SWIGPYTHON)
+  return Target_String("io");
+#endif
+  }
+
+static Target_Type error_invalid()
+  {
+#if defined(SWIGRUBY)
+    static VALUE value = Qnil;
+    if (value == Qnil)
+      value = ID2SYM(rb_intern("invalid"));
+    return value;
+#endif
+#if defined(SWIGPYTHON)
+  return Target_String("invalid");
+#endif
+  }
+
+/*
+ * This is what makes people hate the ZYPP API. Why can't there
+ * be _one_ Error type ?!
+ */
+static Target_Type
+remove_error2target(target::rpm::RemoveResolvableReport::Error error)
+{
+  Target_Type e;
+  switch(error) {
+    case target::rpm::RemoveResolvableReport::NO_ERROR:  e = error_no_error(); break;
+    case target::rpm::RemoveResolvableReport::NOT_FOUND: e = error_not_found(); break;
+    case target::rpm::RemoveResolvableReport::IO:        e = error_io(); break;
+    case target::rpm::RemoveResolvableReport::INVALID:   e = error_invalid(); break;
+  }
+  return e;
+}
+
+
+/*
+ * This is what makes people hate the ZYPP API. Why can't there
+ * be _one_ Action type ?!
+ */
+static target::PatchScriptReport::Action
+target2patch_script_action(Target_Type a)
+{
+#if defined(SWIGPYTHON)
+  const char *s;
+  if (!PyString_Check(a)) {
+    SWIG_exception_fail(SWIG_TypeError, "Expected string type");
+  }
+  s = PyString_AsString(a);
+  if (!strcmp(s, "abort"))
+    return zypp::target::PatchScriptReport::ABORT;
+  else if (!strcmp(s, "retry"))
+    return zypp::target::PatchScriptReport::RETRY;
+  else if (!strcmp(s, "ignore"))
+    return zypp::target::PatchScriptReport::IGNORE;
+  SWIG_exception_fail(SWIG_ArgError(SWIG_ValueError), "Expected \"abort\", \"retry\"  or \"ignore\"");
+#endif
+#if defined(SWIGRUBY)
+  if (a == action_abort_sym())
+    return zypp::target::PatchScriptReport::ABORT;
+  else if (a == action_retry_sym())
+    return zypp::target::PatchScriptReport::RETRY;
+  else if (a == action_ignore_sym())
+    return zypp::target::PatchScriptReport::IGNORE;
+  SWIG_exception_fail(SWIG_ArgError(SWIG_ValueError), "Expected :abort, :retry  or :ignore");
+#endif
+fail:
+  return zypp::target::PatchScriptReport::ABORT;
+}
+
+
+static target::rpm::RemoveResolvableReport::Action
+target2removal_action(Target_Type a)
+{
+#if defined(SWIGPYTHON)
+  const char *s;
+  if (!PyString_Check(a)) {
+    SWIG_exception_fail(SWIG_TypeError, "Expected string type");
+  }
+  s = PyString_AsString(a);
+  if (!strcmp(s, "abort"))
+    return zypp::target::rpm::RemoveResolvableReport::ABORT;
+  else if (!strcmp(s, "retry"))
+    return zypp::target::rpm::RemoveResolvableReport::RETRY;
+  else if (!strcmp(s, "ignore"))
+    return zypp::target::rpm::RemoveResolvableReport::IGNORE;
+  SWIG_exception_fail(SWIG_ArgError(SWIG_ValueError), "Expected \"abort\", \"retry\"  or \"ignore\"");
+#endif
+#if defined(SWIGRUBY)
+  if (a == action_abort_sym())
+    return zypp::target::rpm::RemoveResolvableReport::ABORT;
+  else if (a == action_retry_sym())
+    return zypp::target::rpm::RemoveResolvableReport::RETRY;
+  else if (a == action_ignore_sym())
+    return zypp::target::rpm::RemoveResolvableReport::IGNORE;
+  SWIG_exception_fail(SWIG_ArgError(SWIG_ValueError), "Expected :abort, :retry  or :ignore");
+#endif
+fail:
+  return zypp::target::rpm::RemoveResolvableReport::ABORT;
+}
+
+
+/*
+ * target_call
+ *
+ * Generic helper to call a function of the target language
+ *
+ */
 static Target_Type
 target_call(Target_Type instance, const char *name, int argc, ... )
 {
     va_list ap;
     va_start(ap, argc);
-    printf("Calling %p->%s with %d args\n", (void *)instance, name, argc);
 #if defined(SWIGPYTHON)
     /*
      * Python call with multiple args is like Array
@@ -44,21 +232,20 @@ target_call(Target_Type instance, const char *name, int argc, ... )
 
     if (pyfunc == NULL)
     {
-        printf("%s not defined\n", name);
         PyErr_Print(); 
         PyErr_Clear(); 
         goto cleanup;
     }
     if (! PyCallable_Check(pyfunc)) 
     {
-        printf("%s not callable\n", name);
+        fprintf(stderr,"%s not callable\n", name);
         goto cleanup; 
     }
     
     result = PyObject_CallObject(pyfunc, argv);
     if (PyErr_Occurred())
     {
-        printf("%s returned error\n", name);
+        fprintf(stderr,"%s returned error\n", name);
         PyErr_Print(); 
         PyErr_Clear(); 
         goto cleanup; 
@@ -87,9 +274,11 @@ cleanup:
     return result;
 }
 
+
 /*
  * Patch message
  *
+ * calls 'show_message(zypp::Patch)'
  */
 
 struct PatchMessageReportReceiver : public zypp::callback::ReceiveReport<zypp::target::PatchMessageReport>
@@ -102,7 +291,17 @@ struct PatchMessageReportReceiver : public zypp::callback::ReceiveReport<zypp::t
    */
   virtual bool show( zypp::Patch::constPtr & patch )
   {
-    return true;
+    int result;
+    Target_Type r = SWIG_NewPointerObj((void *)&patch, SWIGTYPE_p_zypp__Patch, 0);
+    Target_Type res = target_call(instance, "patch_message", 1, r );
+#if defined(SWIGPYTHON)
+    result = PyObject_IsTrue(res) ? true : false;
+    if (res) Py_DecRef(res);
+#endif
+#if defined(SWIGRUBY)
+    result = RTEST(res) ? true : false;
+#endif
+    return result;
   }
 };
 
@@ -120,27 +319,81 @@ struct PatchScriptReportReceiver : public zypp::callback::ReceiveReport<zypp::ta
   virtual void start( const zypp::Package::constPtr & package,
                      const zypp::Pathname & path_r ) // script path
   {
+    Target_Type pac = SWIG_NewPointerObj((void *)&(*package), SWIGTYPE_p_zypp__Package, 0);
+    Target_Type path = SWIG_NewPointerObj((void *)&path_r, SWIGTYPE_p_zypp__filesystem__Pathname, 0);
+    Target_Type result = target_call(instance, "patch_script_start", 2, pac, path );
+#if defined(SWIGPYTHON)
+    if (result) Py_DecRef(result);
+    Py_DecRef(path);
+    Py_DecRef(pac);
+#endif
+    return;
   }
 
   /**
-   * Progress provides the script output. If the script is quiet,
+   * Progress provides the script output (Notify=OUTPUT). If the script is quiet,
    * from time to time still-alive pings are sent to the ui. (Notify=PING)
    * Returning \c FALSE aborts script execution.
    */
   virtual bool progress( Notify kind, const std::string &output )
   {
-    return true;
+    int result;
+    Target_Type str = Target_String(output.c_str());
+    Target_Type k;
+    switch(kind) {
+      case OUTPUT:
+#if defined(SWIGPYTHON)
+        k = Target_String("OUTPUT");
+#endif
+#if defined(SWIGRUBY)
+        k = ID2SYM(rb_intern("OUTPUT"));
+#endif
+        break;
+      case PING:
+#if defined(SWIGPYTHON)
+        k = Target_String("PING");
+#endif
+#if defined(SWIGRUBY)
+        k = ID2SYM(rb_intern("PING"));
+#endif
+        break;
+    }
+    Target_Type res = target_call(instance, "patch_script_progress", 2, k, str );
+#if defined(SWIGPYTHON)
+    result = PyObject_IsTrue(res) ? true : false;
+    if (res) Py_DecRef(res);
+    Py_DecRef(k);
+    Py_DecRef(str);
+#endif
+#if defined(SWIGRUBY)
+    result = RTEST(res) ? true : false;
+#endif
+    return result;
   }
 
-  /** Report error. */
+  /** Report patch script error.
+   */
   virtual Action problem( const std::string & description )
   {
-    return zypp::target::PatchScriptReport::ABORT;
+    Action result;
+    Target_Type str = Target_String(description.c_str());
+    Target_Type res = target_call(instance, "patch_script_problem", 1, str );    
+    result = target2patch_script_action(res);
+#if defined(SWIGPYTHON)
+    Py_DecRef(str);
+    if (res) Py_DecRef(res);
+#endif
+    return result;
   }
 
-  /** Report success. */
+  /** Patch script finish. */
   virtual void finish()
   {
+    Target_Type res = target_call(instance, "patch_script_finish", 0 );    
+#if defined(SWIGPYTHON)
+    if (res) Py_DecRef(res);
+#endif
+    return;
   }
 };
 
@@ -155,12 +408,12 @@ struct RemoveResolvableReportReceiver : public zypp::callback::ReceiveReport<zyp
 
   Target_Type instance;
 
-/*  virtual void start( const zypp::Resolvable *resolvable ) */
   virtual void start( Resolvable::constPtr resolvable )
   {
     Target_Type r = SWIG_NewPointerObj((void *)&(*resolvable), SWIGTYPE_p_zypp__Resolvable, 0);
     Target_Type result = target_call(instance, "removal_start", 1, r );
 #if defined(SWIGPYTHON)
+    Py_DecRef(r);
     if (result) Py_DecRef(result);
 #endif
     return;
@@ -168,16 +421,52 @@ struct RemoveResolvableReportReceiver : public zypp::callback::ReceiveReport<zyp
 
   virtual bool progress(int value, zypp::Resolvable::constPtr resolvable)
   {
-    return true;
+    bool result;
+    Target_Type r = SWIG_NewPointerObj((void *)&(*resolvable), SWIGTYPE_p_zypp__Resolvable, 0);
+    Target_Type v = Target_Int(value);
+    Target_Type res = target_call(instance, "removal_progress", 2, r, v );
+#if defined(SWIGPYTHON)
+    result = PyObject_IsTrue(res) ? true : false;
+    Py_DecRef(v);
+    Py_DecRef(r);
+    if (res) Py_DecRef(res);
+#endif
+#if defined(SWIGRUBY)
+    result = RTEST(res) ? true : false;
+#endif
+    return result;
   }
 
-  virtual Action problem( zypp::Resolvable::constPtr resolvable, Error error, const std::string & description )
+  virtual Action problem( zypp::Resolvable::constPtr resolvable, target::rpm::RemoveResolvableReport::Error error, const std::string & description )
   {
-    return RemoveResolvableReportReceiver::ABORT;
+    Action result;
+    Target_Type r = SWIG_NewPointerObj((void *)&(*resolvable), SWIGTYPE_p_zypp__Resolvable, 0);
+    Target_Type e = remove_error2target(error);
+    Target_Type d = Target_String(description.c_str());
+    Target_Type res = target_call(instance, "removal_problem", 3, r, e, d );
+    result = target2removal_action(res);
+#if defined(SWIGPYTHON)
+    if (res) Py_DecRef(res);
+    Py_DecRef(d);
+    Py_DecRef(e);
+    Py_DecRef(r);
+#endif
+    return result;
   }
 
   virtual void finish( zypp::Resolvable::constPtr resolvable, Error error, const std::string & reason )
   {
+    Target_Type r = SWIG_NewPointerObj((void *)&(*resolvable), SWIGTYPE_p_zypp__Resolvable, 0);
+    Target_Type e = remove_error2target(error);
+    Target_Type d = Target_String(reason.c_str());
+    Target_Type res = target_call(instance, "removal_finish", 3, r, e, d );
+#if defined(SWIGPYTHON)
+    if (res) Py_DecRef(res);
+    Py_DecRef(d);
+    Py_DecRef(e);
+    Py_DecRef(r);
+#endif
+    return;
   }
 };
 
@@ -220,3 +509,4 @@ struct InstallResolvableReportReceiver : public zypp::callback::ReceiveReport<zy
 %}
 
 %include "CommitCallbacks.h"
+
index 2dd6f38..ca7446a 100644 (file)
@@ -165,11 +165,11 @@ class CommitCallbacksEmitter {
       _patch_script->start(package, path_r);
     }
        
-         /**
-          *    * Progress provides the script output. If the script is quiet,
-          *    * from time to time still-alive pings are sent to the ui. (Notify=PING)
-          *    * Returning \c FALSE aborts script execution.
-          *    */
+    /**
+     * Progress provides the script output. If the script is quiet,
+     * from time to time still-alive pings are sent to the ui. (Notify=PING)
+     * Returning \c FALSE aborts script execution.
+     */
     bool script_progress( target::PatchScriptReport::Notify kind, const std::string &output )
     {
       return _patch_script->progress(kind, output);
@@ -188,3 +188,5 @@ class CommitCallbacksEmitter {
     }
        
 };
+
+#define REMOVE_NO_ERROR target::rpm::RemoveResolvableReport::NO_ERROR
index 32852f7..b7cc7d4 100644 (file)
@@ -41,6 +41,21 @@ removals = 0
 #
 # TODO: provide a complete list of function names and parameters
 #
+# I. Patch message
+#   patch_message(zypp::Patch) - show patch message
+#
+# II. Patch script
+#   patch_script_start(zypp::Package, String)
+#   patch_script_progress(zypp::Notify, String)
+#   patch_script_problem(String)
+#   patch_script_finish()
+#
+# III. Removal
+#   removal_start(zypp::Resolvable) - start of resolvable uninstall
+#   removal_progress(zypp::Resolvable, Integer) - progress in percent
+#   removal_problem(zypp::Resolvable, zypp::Error, String) - problem report
+#   removal_finish(zypp::Resolvable, zypp::Error, String) - uninstall finish
+#   
 
 class CommitReceiver:
   #
@@ -53,75 +68,104 @@ class CommitReceiver:
     removals += 1
     print "Starting to remove ", resolvable
 
+  #
+  # removal_progress() is called during a resolvable (typically package) uninstall
+  #   and be passed the resolvable to-be-removed and a percentage value
+  #    
+  def removal_progress(self, resolvable, percentage):
+    assert percentage == 42
+    print "Remove of ", resolvable, " at ", percentage, "%"
+
 #
 # Testcase for Callbacks
 #
     
 class CommitCallbacksTestCase(unittest.TestCase):
-    # this will test the remove callback
-    def testRemoveCallback(self):
+    def setUp(self):
         #
         # Normal zypp startup
         #
-        Z = zypp.ZYppFactory_instance().getZYpp()
-        Z.initializeTarget( zypp.Pathname("/") )
-        Z.target().load();
+        self.Z = zypp.ZYppFactory_instance().getZYpp()
+        self.Z.initializeTarget( zypp.Pathname("/") )
+        self.Z.target().load()
 
         # The 'zypp.CommitCallbacksEmitter()' is a test/debug class
         # which can be used to trigger various callbacks
         # (This is callback test code - we cannot do an actual package uninstall here!)
-        commit_callbacks_emitter = zypp.CommitCallbacksEmitter()
+        self.commit_callbacks_emitter = zypp.CommitCallbacksEmitter()
 
         #
         # create an instance of our CommitReceiver class defined above
         #
-        commit_receiver = CommitReceiver()
+        self.commit_receiver = CommitReceiver()
 
         # zypp.CommitCallbacks is the callback 'handler' which must be informed
         # about the receiver
-        commit_callbacks = zypp.CommitCallbacks()
+        self.commit_callbacks = zypp.CommitCallbacks()
 
         #
         # Ensure that no other receiver is registered
         #
-        assert None == commit_callbacks.receiver()
+        assert None == self.commit_callbacks.receiver()
 
         #
         # Connect the receiver instance with the callback handler
         #
-        commit_callbacks.connect(commit_receiver)
+        self.commit_callbacks.connect(self.commit_receiver)
 
         #
         # Ensure that its set correctly
         #
-        assert commit_receiver == commit_callbacks.receiver()
+        assert self.commit_receiver == self.commit_callbacks.receiver()
+
+    def tearDown(self):
+        #
+        # Disconnect the receiver from the callback handler
+        #
+        self.commit_callbacks.disconnect()
+
+        #
+        # Ensure that the disconnect was successful
+        #
+        assert None == self.commit_callbacks.receiver()
+
+    # test patch message
+    def testPatchMessageCallback(self):
+        #
+        # Ugh, this would need a patch with a message :-/
+        #
+        # FIXME
+        assert True
+
+    # test patch script
+    def testPatchScriptCallback(self):
+        #
+        # Ugh, this would need a patch with a script :-/
+        #
+        # FIXME
+        assert True
+
+    # this will test the remove callback
+    def testRemoveCallback(self):
 
         #
         # Loop over pool - just to get real instances of Resolvable
         #
-        for item in Z.pool():
+        for item in self.Z.pool():
             print "Emitting removal of ", item.resolvable()
             #
             # Use the zypp.CommitCallbacksEmitter to fake an actual package removal
             #
-            commit_callbacks_emitter.remove_start(item.resolvable())
-            print "Done"
+            resolvable = item.resolvable()
+            self.commit_callbacks_emitter.remove_start(resolvable)
+            self.commit_callbacks_emitter.remove_progress(resolvable, 42)
+#            self.commit_callbacks_emitter.remove_problem(resolvable, zypp.REMOVE_NO_ERROR, "All fine")
+#            self.commit_callbacks_emitter.remove_finish(resolvable, zypp.REMOVE_NO_ERROR, "Done")
             break # one is sufficient
-
         #
         # Did the actual callback got executed ?
         #
         assert removals == 1
 
-        #
-        # Disconnect the receiver from the callback handler
-        #
-        commit_callbacks.disconnect()
-
-        #
-        # Ensure that the disconnect was successful
-        #
-        assert None == commit_callbacks.receiver()
-
 if __name__ == '__main__':
   unittest.main()
index 1dbd408..74767ce 100644 (file)
@@ -23,6 +23,23 @@ class CommitCallbacksTest < Test::Unit::TestCase
     # In Ruby the class is also an object, so we connect to the class
     commit_callbacks.connect CommitReceiver
     assert_equal CommitReceiver, commit_callbacks.receiver
+    
+    z = Zypp::ZYppFactory::instance.getZYpp
+
+    z.initializeTarget(Zypp::Pathname.new("/"))
+    t = z.target
+    t.load
+    t.buildCache
+    
+    emitter = Zypp::CommitCallbacksEmitter.new
+    p = z.pool
+    p.each do |item|
+      puts "Emitting removal of ", item
+      puts item.methods.inspect
+      emitter.remove_start(item)
+      break
+    end                                                  
+    
     commit_callbacks.disconnect
     assert_equal nil, commit_callbacks.receiver
   end