diff --git a/src/mio.py b/src/mio.py
index 31152143df87403614fb162d420cfafc3ac3def7..465cf50c465c807fd073780065ed9e103cefdfd6 100755
--- a/src/mio.py
+++ b/src/mio.py
@@ -287,7 +287,8 @@ if __name__ == '__main__':
                                 options.exclude_rpm)
  
         log(NORMAL, "Testing files")
-        installer.test_groups(options.prefix)
+        installer.test_groups(options.prefix,
+                              (options.backup, options.backup_suffix))
         
     if options.install:
         installer = mio.installer.Installer(rules)
diff --git a/src/mio/installer.py b/src/mio/installer.py
index 7eed9762cc273dd6dc9eee5ace2cfd06a6c8ebce..a945b0f01e0141fd7d7d03b2e83662214d4917c7 100755
--- a/src/mio/installer.py
+++ b/src/mio/installer.py
@@ -7,12 +7,27 @@ from mio.node import *
 from mio.exception import *
 from mio.log import log, VERBOSE, CHATTY
 
+try:
+    from collections import OrderedDict
+    pass
+except ImportError:
+    try:
+        # 2.6 fallback if installed
+        from ordereddict import OrderedDict
+        pass
+    except ImportError:
+        # Use unordered dict if everything else fails
+        OrderedDict = dict
+        log(NORMAL, "No OrderedDict found")
+        pass
+    pass
+
 class Installer:
     def __init__(self, repository):
         self.repository = repository
         self.rpmdir = []
         self.group = {}
-        self.rpm = {}
+        self.rpm = OrderedDict()
         self.dir = {}
         self.file = {}
         self.symlink = {}
@@ -160,7 +175,7 @@ class Installer:
         for r in self.rpm:
             rpms.append(str(r.name))
             pass
-        rpms.sort()
+        #rpms.sort()
         return rpms
             
     def install_rpms(self, path, exclude):
@@ -175,7 +190,7 @@ class Installer:
         def not_excluded(name):
             return not any(map(lambda x: fnmatch.fnmatch(name, x), exclude))
         rpm = filter(not_excluded, rpm)
-        rpm.sort()
+        #rpm.sort()
         print rpm
         mio.yum.run(path + self.rpmdir, rpm, exclude)
         version_db = mio.rpmDB.VersionDB()
@@ -191,8 +206,8 @@ class Installer:
         return result
             
     def install_groups(self, prefix, backup):
-        for g in self.group:
-            g.do_pre(prefix, backup)
+#        for g in self.group:
+#            g.do_pre(prefix, backup)
         for g in self.group:
             g.install(prefix, backup)
 
@@ -200,9 +215,9 @@ class Installer:
         log(NORMAL, "Excluding rpms: "+ ",".join(exclude))
         pass
     
-    def test_groups(self, prefix):
+    def test_groups(self, prefix, backup):
         for g in self.group:
-            g.test(prefix)
+            g.test(prefix, backup)
 
 def exclusion_closure(exclude, target):
     if not target._exclude_:
diff --git a/src/mio/node.py b/src/mio/node.py
index 71d741cdbec3359d226915b37fa4e3a401bf4d4e..b6704b8293c5021cbc896f31fa97e5d7392e122f 100755
--- a/src/mio/node.py
+++ b/src/mio/node.py
@@ -49,10 +49,17 @@ class target_node:
     def __hash__(self):
         return self.name.__hash__()
 
-    def trigged(self, prefix):
+    def trigged(self, prefix, backup, install=False):
         if self.is_trigged == None:
             try:
-                self.is_trigged = self.do_trig(prefix)
+                self.is_trigged = self.do_trig(prefix, backup, install)
+                if self.is_trigged:
+                    if install:
+                        self.do_install(prefix, backup)
+                    else:
+                        self.do_test(prefix)
+                    pass
+                pass
             except SystemExit:
                 raise
             except Exception, e:
@@ -60,14 +67,12 @@ class target_node:
         return self.is_trigged
     
     def install(self, prefix, backup):
-        if self.trigged(prefix) and not self.is_installed:
+        if not self.is_installed and self.trigged(prefix, backup, install=True):
             self.is_installed = True
-            self.do_install(prefix, backup)
     
-    def test(self, prefix):
-        if self.trigged(prefix) and not self.is_tested:
+    def test(self, prefix, backup):
+        if not self.is_tested and self.trigged(prefix, backup, install=False):
             self.is_tested = True
-            self.do_test(prefix)
     
         
 class group_node(target_node):
@@ -109,31 +114,33 @@ class group_node(target_node):
     def add_post(self, post):
         self.post.append(post)
 
-    def do_trig(self, prefix):
-        for t in self.target:
-            if t.trigged(prefix):
-                return True
+    def do_trig(self, prefix, backup, install):
+        result = False
         for d in self.dependency:
-            if d.trigged(prefix):
-                return True
-        return False
+            if d.trigged(prefix, backup, install):
+                result = True
+                pass
+            pass
+        for t in self.target:
+            if t.trigged(prefix, backup, install):
+                result = True
+                pass
+            pass
+        return result
 
-    def do_pre(self, prefix, backup):
-        if self.trigged(prefix):
-            for a in self.pre:
-                a.run("pre")
-    
     def do_install(self, prefix, backup):
-        for d in self.dependency:
-            d.install(prefix, backup)
+#        for d in self.dependency:
+#            d.install(prefix, backup)
+        for a in self.pre:
+            a.run("pre")
         for t in self.target:
             t.install(prefix, backup)
         for a in self.post:
             a.run("post")
 
     def do_test(self, prefix):
-        for d in self.dependency:
-            d.test(prefix)
+#        for d in self.dependency:
+#            d.test(prefix)
         for a in self.pre:
             a.test("pre")
         for t in self.target:
@@ -145,7 +152,7 @@ class rpm_node(target_node):
     def __init__(self, name, target=None, group=None):
         target_node.__init__(self, 'rpm', name, target, group)
 
-    def do_trig(self, prefix):
+    def do_trig(self, prefix, backup, do_pre):
         result = False
         try:
             result = self.old_version != self.new_version
@@ -164,22 +171,24 @@ class dir_node(target_node):
         target_node.__init__(self, 'dir', name, decl, group)
         self.action = []
         self.mode = mio.util.dirmode(decl)
-        self.uid = mio.util.uid(decl)
-        self.gid = mio.util.gid(decl)
+        self.owner = mio.util.owner(decl)
+        self.group = mio.util.group(decl)
         
     def assert_identical(self, other):
         if self.__cmp__(other) != 0:
             raise Exception("%s %s" % (self.name, other.name))
         if self.mode != other.mode:
             raise ModeException(self, other)
-        if self.uid != other.uid:
+        if self.owner != other.owner:
             raise OwnerException(self, other)
-        if self.gid != other.gid:
+        if self.group != other.group:
             raise GroupException(self, other)
     
-    def do_trig(self, prefix):
+    def do_trig(self, prefix, backup, do_pre):
         result = False
         self.target = mio.filecache.locatedir([prefix, self.decl.name[0]])
+        self.uid = mio.util.uid(self.owner)
+        self.gid = mio.util.uid(self.group)
         
         if not os.path.exists(self.target):
             self.action.append(self.mkdir)
@@ -231,8 +240,8 @@ class file_node(target_node):
         self.action = []
         if decl:
             self.mode = mio.util.filemode(decl)
-            self.uid = mio.util.uid(decl)
-            self.gid = mio.util.gid(decl)
+            self.owner = mio.util.owner(decl)
+            self.group = mio.util.group(decl)
             self.delete = parse_boolean(self.decl.delete[0])
             self.touch = parse_boolean(self.decl.touch_when_copied[0])
         
@@ -245,9 +254,9 @@ class file_node(target_node):
             raise SourceException(self, other)
         if self.decl.files[0:] != other.decl.files[0:]:
             raise SourceException(self, other)
-        if self.uid != other.uid:
+        if self.owner != other.owner:
             raise OwnerException(self, other)
-        if self.gid != other.gid:
+        if self.group != other.group:
             raise GroupException(self, other)
         if self.delete != other.delete:
             raise DeleteException(self, other)
@@ -258,10 +267,13 @@ class file_node(target_node):
         f.close()
         return digest
     
-    def do_trig(self, prefix):
+    def do_trig(self, prefix, backup, do_pre):
         result = False
         dir = os.path.dirname(self.decl._url)
         self.target = mio.filecache.locatefile([prefix, self.decl.name[0]])
+        self.uid = mio.util.uid(self.owner)
+        self.gid = mio.util.uid(self.group)
+
         if self.delete:
             if os.path.exists(self.target):
                 if os.path.islink(self.target):
@@ -304,7 +316,6 @@ class file_node(target_node):
                 # No source file, we will only set owner and mode
                 tstat = os.stat(self.target)
         
-    
             if self.uid != tstat.st_uid or self.gid != tstat.st_gid:
                 result = True
                 self.action.append(self.chown)
@@ -392,8 +403,8 @@ class symlink_node(target_node):
         target_node.__init__(self, 'symlink', name, decl, group)
         self.action = []
         if decl:
-            self.uid = mio.util.uid(decl)
-            self.gid = mio.util.gid(decl)
+            self.owner = mio.util.owner(decl)
+            self.group = mio.util.group(decl)
             self.delete = parse_boolean(self.decl.delete[0])                
        
     def assert_identical(self, other):
@@ -401,17 +412,19 @@ class symlink_node(target_node):
             raise Exception("%s %s" % (self.name, other.name))
         if self.decl.value[0] == None or self.decl.value[0] != other.decl.value[0]:
             raise SymlinkValueException(self, other)
-        if self.uid != other.uid:
+        if self.owner != other.owner:
             raise OwnerException(self, other)
-        if self.gid != other.gid:
+        if self.group != other.group:
             raise GroupException(self, other)
         if self.delete != other.delete:
             raise DeleteException(self, other)
     
-    def do_trig(self, prefix):
+    def do_trig(self, prefix, backup, do_pre):
         result = False
         self.target = mio.filecache.locatedir([prefix, self.decl.name[0]])
         
+        self.uid = mio.util.uid(self.owner)
+        self.gid = mio.util.uid(self.group)
         if self.delete:
             if os.path.exists(self.target):
                 if not os.path.islink(self.target):
diff --git a/src/mio/parser.py b/src/mio/parser.py
index 111b8c31da0fd9cd5dba8f9a4e929437184f347b..2c4e4a760a16e3f5525b2e27965ffe4bfa645946 100755
--- a/src/mio/parser.py
+++ b/src/mio/parser.py
@@ -1,7 +1,9 @@
+import calendar
 import os
 import stat
 import sys
 import time
+import xml.sax
 import xml.parsers.expat
 import traceback
 import urlparse
@@ -378,7 +380,10 @@ class Node:
             result += "%s/>\n" % line
         else:
             result += "%s>\n" % line
-            result += xml_escape("".join(self._char))
+            if self._char:
+                result += '\n'.join(self._char)
+                result += '\n'
+                pass
             child = self._children
             child.sort(TagSortWrapper(self, tag_sort))
             for c in child:
@@ -431,6 +436,8 @@ class Comment:
         self._content = content
         self._line = line
         self._parent = None
+        self._tag = ''
+        self._attribute = { '': content}
 
     def __copy__(self):
         """Copy self"""
@@ -452,11 +459,12 @@ class Comment:
         return self._xml()
 
 class Parser:
-    def __init__(self, url, include_comments=False):
-        """Parse the given url inti .tree
+    def __init__(self, data, url=None, mtime=None, include_comments=False):
+        """Parse the given url into .tree
 
         augment the tree with
-          ._mtime == url modification time if url is a file, 0 otherwise
+          ._mtime == url modification time if url is a file, 
+                     current time otherwise
           ._url   == the url that was parsed
         """
         self.include_comments = include_comments
@@ -471,15 +479,8 @@ class Parser:
 
         # Parse the url
         self.current = [ self ]
-        (scheme, host, path, param, query, frag) = urlparse.urlparse(url)
         try:
-            if not scheme:
-                self.parser.Parse(open(url).read())
-                pass
-            else:
-                self.parser.Parse(urllib2.urlopen(url).read())
-                pass
-            pass
+            self.parser.Parse(data)
         except xml.parsers.expat.ExpatError:
             reason = xml.parsers.expat.ErrorString(self.parser.ErrorCode)
             line = self.parser.ErrorLineNumber
@@ -487,13 +488,6 @@ class Parser:
             raise Exception("%s at line %d, column %d in %s" %
                             (reason, line, column, url))
             pass
-        # Set the mtime
-        mtime = 0
-        try:
-            mtime = os.stat(url)[stat.ST_MTIME]                
-        except Exception,e:
-            mtime = int(time.time())
-
         self.tree._mtime = mtime
         self.tree._url = url
             
@@ -527,7 +521,28 @@ class Parser:
             parent = self.current[-1]
             parent._add(Comment(data, self.parser.CurrentLineNumber))
 
-def parse(url):
-    """Parse the given 'url' returning an easily traversable tree"""
-    return Parser(url).tree
-
+def parse(source, include_comments=False):
+    """Parse the given 'source' returning an easily traversable tree"""
+    (scheme, host, path, param, query, frag) = urlparse.urlparse(source)
+    if not scheme:
+        data = open(source).read()
+        mtime = os.stat(source)[stat.ST_MTIME]                
+        pass
+    else:
+        u = urllib2.urlopen(source)
+        data = u.read()
+        try:
+            modified = u.info()["Last-Modified"]
+            mtime = calendar.timegm(
+                time.strptime(modified, "%a, %d %b %Y %H:%M:%S %Z"))
+        except:
+            mtime = None
+            pass
+        pass
+    pass
+    return Parser(data, url=source, mtime=mtime, 
+                  include_comments=include_comments).tree
+
+def parse_string(source, include_comments=False):
+    return Parser(source, url=None, mtime=None, 
+                  include_comments=include_comments).tree
diff --git a/src/mio/util.py b/src/mio/util.py
index dcf2d04543b9041c3b764dcdf0ff32e364ac9dd0..e13195267b009c86d446b9791ba39c3f3bcee5a5 100755
--- a/src/mio/util.py
+++ b/src/mio/util.py
@@ -62,40 +62,41 @@ def dirmode(dir):
     mode.append(0700)
     return mode_combine(mode)
 
-def uid(decl):
-    owner = decl.owner[0:]
+def uid(owner):
     if owner == None:
-        owner = os.getuid()
+        uid = os.getuid()
         pass
-    if owner.__class__ != int:
+    elif owner.isdigit():
+        uid = int(owner)
+        pass
+    else:
         try:
-            if owner.isdigit():
-                owner = int(owner)
-                pass
-            else:
-                owner = pwd.getpwnam(owner)[2]
-                pass
+            uid = pwd.getpwnam(owner)[2]
             pass
         except:
             raise Exception("user '%s' not found" % owner)
         pass
-    return owner
+    return uid
 
-def gid(decl):
-    group = decl.group[0:]
+def gid(group):
     if group == None:
-        group = os.getgid()
+        gid = os.getgid()
+        pass
+    elif group.isdigit():
+        gid = int(group)
         pass
-    if group.__class__ != int:
+    else:
         try:
-            if group.isdigit():
-                group = int(group)
-                pass
-            else:
-                group = grp.getgrnam(group)[2]
-                pass
+            gid = grp.getgrnam(group)[2]
             pass
         except:
             raise Exception("group '%s' not found" % group)
         pass
-    return group
+    return gid
+
+def owner(decl):
+    return decl.owner[0:]
+
+def group(decl):
+    return decl.group[0:]
+