1from __future__ import absolute_import
2import unittest
3
4import os
5import six
6import sys
7import os.path
8import time
9from tempfile import mkdtemp
10from shutil import rmtree
11import mozunit
12from mozprocess import processhandler
13
14from nsinstall import nsinstall
15import nsinstall as nsinstall_module
16
17NSINSTALL_PATH = nsinstall_module.__file__
18
19# Run the non-ASCII tests on (a) Windows, or (b) any platform with
20# sys.stdin.encoding set to UTF-8
21import codecs
22
23RUN_NON_ASCII_TESTS = sys.platform == "win32" or (
24    sys.stdin.encoding is not None
25    and codecs.lookup(sys.stdin.encoding) == codecs.lookup("utf-8")
26)
27
28
29class TestNsinstall(unittest.TestCase):
30    """
31    Unit tests for nsinstall.py
32    """
33
34    def setUp(self):
35        self.tmpdir = mkdtemp()
36
37    def tearDown(self):
38        # Unicode strings means non-ASCII children can be deleted properly on
39        # Windows
40        if sys.stdin.encoding is None:
41            tmpdir = six.ensure_text(self.tmpdir)
42        else:
43            tmpdir = six.ensure_text(self.tmpdir, sys.stdin.encoding)
44        rmtree(tmpdir)
45
46    # utility methods for tests
47    def touch(self, file, dir=None):
48        if dir is None:
49            dir = self.tmpdir
50        f = os.path.join(dir, file)
51        open(f, "w").close()
52        return f
53
54    def mkdirs(self, dir):
55        d = os.path.join(self.tmpdir, dir)
56        os.makedirs(d)
57        return d
58
59    def test_nsinstall_D(self):
60        "Test nsinstall -D <dir>"
61        testdir = os.path.join(self.tmpdir, "test")
62        self.assertEqual(nsinstall(["-D", testdir]), 0)
63        self.assert_(os.path.isdir(testdir))
64
65    def test_nsinstall_basic(self):
66        "Test nsinstall <file> <dir>"
67        testfile = self.touch("testfile")
68        testdir = self.mkdirs("testdir")
69        self.assertEqual(nsinstall([testfile, testdir]), 0)
70        self.assert_(os.path.isfile(os.path.join(testdir, "testfile")))
71
72    def test_nsinstall_basic_recursive(self):
73        "Test nsinstall <dir> <dest dir>"
74        sourcedir = self.mkdirs("sourcedir")
75        self.touch("testfile", sourcedir)
76        Xfile = self.touch("Xfile", sourcedir)
77        copieddir = self.mkdirs("sourcedir/copieddir")
78        self.touch("testfile2", copieddir)
79        Xdir = self.mkdirs("sourcedir/Xdir")
80        self.touch("testfile3", Xdir)
81
82        destdir = self.mkdirs("destdir")
83
84        self.assertEqual(nsinstall([sourcedir, destdir, "-X", Xfile, "-X", Xdir]), 0)
85
86        testdir = os.path.join(destdir, "sourcedir")
87        self.assert_(os.path.isdir(testdir))
88        self.assert_(os.path.isfile(os.path.join(testdir, "testfile")))
89        self.assert_(not os.path.exists(os.path.join(testdir, "Xfile")))
90        self.assert_(os.path.isdir(os.path.join(testdir, "copieddir")))
91        self.assert_(os.path.isfile(os.path.join(testdir, "copieddir", "testfile2")))
92        self.assert_(not os.path.exists(os.path.join(testdir, "Xdir")))
93
94    def test_nsinstall_multiple(self):
95        "Test nsinstall <three files> <dest dir>"
96        testfiles = [
97            self.touch("testfile1"),
98            self.touch("testfile2"),
99            self.touch("testfile3"),
100        ]
101        testdir = self.mkdirs("testdir")
102        self.assertEqual(nsinstall(testfiles + [testdir]), 0)
103        for f in testfiles:
104            self.assert_(os.path.isfile(os.path.join(testdir, os.path.basename(f))))
105
106    def test_nsinstall_dir_exists(self):
107        "Test nsinstall <dir> <dest dir>, where <dest dir>/<dir> already exists"
108        srcdir = self.mkdirs("test")
109        destdir = self.mkdirs("testdir/test")
110        self.assertEqual(nsinstall([srcdir, os.path.dirname(destdir)]), 0)
111        self.assert_(os.path.isdir(destdir))
112
113    def test_nsinstall_t(self):
114        "Test that nsinstall -t works (preserve timestamp)"
115        testfile = self.touch("testfile")
116        testdir = self.mkdirs("testdir")
117        # set mtime to now - 30 seconds
118        t = int(time.time()) - 30
119        os.utime(testfile, (t, t))
120        self.assertEqual(nsinstall(["-t", testfile, testdir]), 0)
121        destfile = os.path.join(testdir, "testfile")
122        self.assert_(os.path.isfile(destfile))
123        self.assertEqual(os.stat(testfile).st_mtime, os.stat(destfile).st_mtime)
124
125    @unittest.skipIf(sys.platform == "win32", "Windows doesn't have real file modes")
126    def test_nsinstall_m(self):
127        "Test that nsinstall -m works (set mode)"
128        testfile = self.touch("testfile")
129        mode = 0o600
130        os.chmod(testfile, mode)
131        testdir = self.mkdirs("testdir")
132        self.assertEqual(
133            nsinstall(["-m", "{0:04o}".format(mode), testfile, testdir]), 0
134        )
135        destfile = os.path.join(testdir, "testfile")
136        self.assert_(os.path.isfile(destfile))
137        self.assertEqual(os.stat(testfile).st_mode, os.stat(destfile).st_mode)
138
139    def test_nsinstall_d(self):
140        "Test that nsinstall -d works (create directories in target)"
141        # -d makes no sense to me, but ok!
142        testfile = self.touch("testfile")
143        testdir = self.mkdirs("testdir")
144        destdir = os.path.join(testdir, "subdir")
145        self.assertEqual(nsinstall(["-d", testfile, destdir]), 0)
146        self.assert_(os.path.isdir(os.path.join(destdir, "testfile")))
147
148    @unittest.skipIf(not RUN_NON_ASCII_TESTS, "Skipping non ascii tests")
149    def test_nsinstall_non_ascii(self):
150        "Test that nsinstall handles non-ASCII files"
151        filename = u"\u2325\u3452\u2415\u5081"
152        testfile = self.touch(filename)
153        testdir = self.mkdirs(u"\u4241\u1D04\u1414")
154        self.assertEqual(
155            nsinstall([testfile.encode("utf-8"), testdir.encode("utf-8")]), 0
156        )
157
158        destfile = os.path.join(testdir, filename)
159        self.assert_(os.path.isfile(destfile))
160
161    # Executing nsinstall.py with python 2 is not supported.
162    @unittest.skipIf(
163        not RUN_NON_ASCII_TESTS or sys.version_info[0] == 2, "Skipping non ascii tests"
164    )
165    def test_nsinstall_non_ascii_subprocess(self):
166        "Test that nsinstall as a subprocess handles non-ASCII files"
167        filename = u"\u2325\u3452\u2415\u5081"
168        testfile = self.touch(filename)
169        testdir = self.mkdirs(u"\u4241\u1D04\u1414")
170        # We don't use subprocess because it can't handle Unicode on
171        # Windows <http://bugs.python.org/issue1759845>. mozprocess calls
172        # CreateProcessW directly so it's perfect.
173        p = processhandler.ProcessHandlerMixin(
174            [sys.executable, NSINSTALL_PATH, testfile, testdir]
175        )
176        p.run()
177        rv = p.wait()
178
179        self.assertEqual(rv, 0)
180        destfile = os.path.join(testdir, filename)
181        self.assert_(os.path.isfile(destfile))
182
183    # TODO: implement -R, -l, -L and test them!
184
185
186if __name__ == "__main__":
187    mozunit.main()
188