bug 482733 - nsinstall.py should support copying directories recursively - refactor nsinstall.py and add unittests. r=pike
authorTed Mielczarek <ted.mielczarek@gmail.com>
Thu, 12 Mar 2009 08:46:38 -0400
changeset 26093 f01a5fd071f3b55a5a4a6b928a883e853637cda8
parent 26092 2d7dde25e45fdf7925be675a2e803b7446c73cfc
child 26094 6d2ef6b135fa996edca8d3c28d88579b97b6909b
push idunknown
push userunknown
push dateunknown
reviewerspike
bugs482733
milestone1.9.2a1pre
bug 482733 - nsinstall.py should support copying directories recursively - refactor nsinstall.py and add unittests. r=pike
config/Makefile.in
config/nsinstall.py
config/tests/unit-nsinstall.py
--- a/config/Makefile.in
+++ b/config/Makefile.in
@@ -149,21 +149,21 @@ endif
 
 FORCE:
 
 ifdef MKDEPEND_DIR
 clean clobber realclean clobber_all::
 	cd $(MKDEPEND_DIR); $(MAKE) $@
 endif
 
-PYUNITS := unit-Expression.py unit-Preprocessor.py
+PYUNITS := unit-Expression.py unit-Preprocessor.py unit-nsinstall.py
 
-check:: check-preprocessor check-jar-mn
+check:: check-python-modules check-jar-mn
 
-check-preprocessor::
+check-python-modules::
 	@$(EXIT_ON_ERROR) \
 	for test in $(PYUNITS); do \
 	  $(PYTHON) $(srcdir)/tests/$$test ; \
 	done
 
 check-jar-mn::
 	make -C tests/src-simple check-jar
 	make -C tests/src-simple check-flat
--- a/config/nsinstall.py
+++ b/config/nsinstall.py
@@ -44,96 +44,101 @@
 # all related options.
 
 from optparse import OptionParser
 import os
 import os.path
 import sys
 import shutil
 
-usage = "usage: %prog [options] arg1 [arg2 ...] target-directory"
-p = OptionParser(usage=usage)
+def nsinstall(argv):
+  usage = "usage: %prog [options] arg1 [arg2 ...] target-directory"
+  p = OptionParser(usage=usage)
 
-p.add_option('-D', action="store_true",
-             help="Create a single directory only")
-p.add_option('-t', action="store_true",
-             help="Preserve time stamp")
-p.add_option('-m', action="store",
-             help="Set mode", metavar="mode")
-p.add_option('-d', action="store_true",
-             help="Create directories in target")
-p.add_option('-R', action="store_true",
-             help="Use relative symbolic links (ignored)")
-p.add_option('-l', action="store_true",
-             help="Create link (ignored)")
-p.add_option('-L', action="store", metavar="linkprefix",
-             help="Link prefix (ignored)")
+  p.add_option('-D', action="store_true",
+               help="Create a single directory only")
+  p.add_option('-t', action="store_true",
+               help="Preserve time stamp")
+  p.add_option('-m', action="store",
+               help="Set mode", metavar="mode")
+  p.add_option('-d', action="store_true",
+               help="Create directories in target")
+  p.add_option('-R', action="store_true",
+               help="Use relative symbolic links (ignored)")
+  p.add_option('-l', action="store_true",
+               help="Create link (ignored)")
+  p.add_option('-L', action="store", metavar="linkprefix",
+               help="Link prefix (ignored)")
 
-# The remaining arguments are not used in our tree, thus they're not
-# implented.
-def BadArg(option, opt, value, parser):
-  parser.error('option not supported: %s' % opt)
+  # The remaining arguments are not used in our tree, thus they're not
+  # implented.
+  def BadArg(option, opt, value, parser):
+    parser.error('option not supported: %s' % opt)
+    
+  p.add_option('-C', action="callback", metavar="CWD",
+               callback=BadArg,
+               help="NOT SUPPORTED")
+  p.add_option('-o', action="callback", callback=BadArg,
+               help="Set owner (NOT SUPPORTED)", metavar="owner")
+  p.add_option('-g', action="callback", callback=BadArg,
+               help="Set group (NOT SUPPORTED)", metavar="group")
 
-p.add_option('-C', action="callback", metavar="CWD",
-             callback=BadArg,
-             help="NOT SUPPORTED")
-p.add_option('-o', action="callback", callback=BadArg,
-             help="Set owner (NOT SUPPORTED)", metavar="owner")
-p.add_option('-g', action="callback", callback=BadArg,
-             help="Set group (NOT SUPPORTED)", metavar="group")
+  (options, args) = p.parse_args(argv)
 
-(options, args) = p.parse_args()
-
-if options.m:
-  # mode is specified
-  try:
-    options.m = int(options.m, 8)
-  except:
-    sys.stderr.write('nsinstall: ' + options.m + ' is not a valid mode\n')
-    sys.exit(1)
+  if options.m:
+    # mode is specified
+    try:
+      options.m = int(options.m, 8)
+    except:
+      sys.stderr.write('nsinstall: ' + options.m + ' is not a valid mode\n')
+      return 1
 
-# just create one directory?
-if options.D:
-  if len(args) != 1:
-    sys.exit(1)
-  if os.path.exists(args[0]):
-    if not os.path.isdir(args[0]):
-      sys.stderr.write('nsinstall: ' + args[0] + ' is not a directory\n')
-      sys.exit(1)
+  # just create one directory?
+  if options.D:
+    if len(args) != 1:
+      return 1
+    if os.path.exists(args[0]):
+      if not os.path.isdir(args[0]):
+        sys.stderr.write('nsinstall: ' + args[0] + ' is not a directory\n')
+        sys.exit(1)
+      if options.m:
+        os.chmod(args[0], options.m)
+      sys.exit()
     if options.m:
-      os.chmod(args[0], options.m)
-    sys.exit()
-  if options.m:
-    os.makedirs(args[0], options.m)
-  else:
-    os.makedirs(args[0])
-  sys.exit()
+      os.makedirs(args[0], options.m)
+    else:
+      os.makedirs(args[0])
+    return 0
 
-# nsinstall arg1 [...] directory
-if len(args) < 2:
-  p.error('not enough arguments')
+  # nsinstall arg1 [...] directory
+  if len(args) < 2:
+    p.error('not enough arguments')
 
-# set up handler
-if options.d:
-  # we're supposed to create directories
-  def handleTarget(srcpath, targetpath):
-    # target directory was already created, just use mkdir
-    os.mkdir(dest)
-else:
-  # we're supposed to copy files
-  def handleTarget(srcpath, targetpath):
-    if options.t:
-      shutil.copy2(srcpath, targetpath)
-    else:
-      shutil.copy(srcpath, targetpath)
+  # set up handler
+  if options.d:
+    # we're supposed to create directories
+    def handleTarget(srcpath, targetpath):
+      # target directory was already created, just use mkdir
+      os.mkdir(dest)
+  else:
+    # we're supposed to copy files
+    def handleTarget(srcpath, targetpath):
+      if options.t:
+        shutil.copy2(srcpath, targetpath)
+      else:
+        shutil.copy(srcpath, targetpath)
 
-# the last argument is the target directory
-target = args.pop()
-# ensure target directory
-if not os.path.isdir(target):
-  os.makedirs(target)
+  # the last argument is the target directory
+  target = args.pop()
+  # ensure target directory
+  if not os.path.isdir(target):
+    os.makedirs(target)
 
-for f in args:
-  dest = os.path.join(target,
-                      os.path.basename(os.path.normpath(f)))
-  handleTarget(f, dest)
-  if options.m:
-    os.chmod(dest, options.m)
+  for f in args:
+    dest = os.path.join(target,
+                        os.path.basename(os.path.normpath(f)))
+    handleTarget(f, dest)
+    if options.m:
+      os.chmod(dest, options.m)
+  return 0
+
+if __name__ == '__main__':
+  sys.exit(nsinstall(sys.argv[1:]))
new file mode 100644
--- /dev/null
+++ b/config/tests/unit-nsinstall.py
@@ -0,0 +1,96 @@
+import unittest
+
+import os, sys, os.path, time
+from tempfile import mkdtemp
+from shutil import rmtree
+sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
+
+from nsinstall import nsinstall
+
+class TestNsinstall(unittest.TestCase):
+    """
+    Unit tests for nsinstall.py
+    """
+    def setUp(self):
+        self.tmpdir = mkdtemp()
+
+    def tearDown(self):
+        rmtree(self.tmpdir)
+
+    # utility methods for tests
+    def touch(self, file, dir=None):
+        if dir is None:
+            dir = self.tmpdir
+        f = os.path.join(dir, file)
+        open(f, 'w').close()
+        return f
+
+    def mkdirs(self, dir):
+        d = os.path.join(self.tmpdir, dir)
+        os.makedirs(d)
+        return d
+
+    def test_nsinstall_D(self):
+        "Test nsinstall -D <dir>"
+        testdir = os.path.join(self.tmpdir, "test")
+        self.assertEqual(nsinstall(["-D", testdir]), 0)
+        self.assert_(os.path.isdir(testdir))
+
+    def test_nsinstall_basic(self):
+        "Test nsinstall <file> <dir>"
+        testfile = self.touch("testfile")
+        testdir = self.mkdirs("testdir")
+        self.assertEqual(nsinstall([testfile, testdir]), 0)
+        self.assert_(os.path.isfile(os.path.join(testdir, "testfile")))
+
+    def test_nsinstall_multiple(self):
+        "Test nsinstall <three files> <dest dir>"
+        testfiles = [self.touch("testfile1"),
+                     self.touch("testfile2"),
+                     self.touch("testfile3")]
+        testdir = self.mkdirs("testdir")
+        self.assertEqual(nsinstall(testfiles + [testdir]), 0)
+        for f in testfiles:
+            self.assert_(os.path.isfile(os.path.join(testdir,
+                                                     os.path.basename(f))))
+
+    def test_nsinstall_t(self):
+        "Test that nsinstall -t works (preserve timestamp)"
+        testfile = self.touch("testfile")
+        testdir = self.mkdirs("testdir")
+        # set mtime to now - 30 seconds
+        t = int(time.time()) - 30
+        os.utime(testfile, (t, t))
+        self.assertEqual(nsinstall(["-t", testfile, testdir]), 0)
+        destfile = os.path.join(testdir, "testfile")
+        self.assert_(os.path.isfile(destfile))
+        self.assertEqual(os.stat(testfile).st_mtime,
+                         os.stat(destfile).st_mtime)
+
+    if sys.platform != "win32":
+        # can't run this test on windows, don't have real file modes there
+        def test_nsinstall_m(self):
+            "Test that nsinstall -m works (set mode)"
+            testfile = self.touch("testfile")
+            mode = 0600
+            os.chmod(testfile, mode)
+            testdir = self.mkdirs("testdir")
+            self.assertEqual(nsinstall(["-m", "%04o" % mode, testfile, testdir]), 0)
+            destfile = os.path.join(testdir, "testfile")
+            self.assert_(os.path.isfile(destfile))
+            self.assertEqual(os.stat(testfile).st_mode,
+                             os.stat(destfile).st_mode)
+
+    def test_nsinstall_d(self):
+        "Test that nsinstall -d works (create directories in target)"
+        # -d makes no sense to me, but ok!
+        testfile = self.touch("testfile")
+        testdir = self.mkdirs("testdir")
+        destdir = os.path.join(testdir, "subdir")
+        self.assertEqual(nsinstall(["-d", testfile, destdir]), 0)
+        self.assert_(os.path.isdir(os.path.join(destdir, "testfile")))
+
+    #TODO: implement -R, -l, -L and test them!
+
+if __name__ == '__main__':
+  unittest.main()