blob: b734adf345f5899fe5fadff14d1f63679914bd16 [file] [log] [blame]
Takuto Ikuta22eee762019-10-16 06:29:48 +00001#!/usr/bin/env vpython
maruel@chromium.org561d4b22013-09-26 21:08:08 +00002# coding=utf-8
maruelea586f32016-04-05 11:11:33 -07003# Copyright 2013 The LUCI Authors. All rights reserved.
maruelf1f5e2a2016-05-25 17:10:39 -07004# Use of this source code is governed under the Apache License, Version 2.0
5# that can be found in the LICENSE file.
maruel@chromium.org561d4b22013-09-26 21:08:08 +00006
maruel9cdd7612015-12-02 13:40:52 -08007import getpass
maruel@chromium.org561d4b22013-09-26 21:08:08 +00008import os
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -04009import StringIO
10import subprocess
maruel@chromium.org561d4b22013-09-26 21:08:08 +000011import sys
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +000012import tempfile
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -040013import time
maruel@chromium.org561d4b22013-09-26 21:08:08 +000014
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +000015# Mutates sys.path.
16import test_env
maruel@chromium.org561d4b22013-09-26 21:08:08 +000017
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +000018# third_party/
Marc-Antoine Ruelf1d827c2014-11-24 15:22:25 -050019from depot_tools import auto_stub
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +000020
maruel@chromium.org561d4b22013-09-26 21:08:08 +000021from utils import file_path
maruel4e732992015-10-16 10:17:21 -070022from utils import fs
maruel@chromium.org561d4b22013-09-26 21:08:08 +000023
24
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -040025def write_content(filepath, content):
maruel4e732992015-10-16 10:17:21 -070026 with fs.open(filepath, 'wb') as f:
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -040027 f.write(content)
28
29
Marc-Antoine Ruelf1d827c2014-11-24 15:22:25 -050030class FilePathTest(auto_stub.TestCase):
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -040031 def setUp(self):
32 super(FilePathTest, self).setUp()
33 self._tempdir = None
34
35 def tearDown(self):
maruel4b14f042015-10-06 12:08:08 -070036 try:
37 if self._tempdir:
maruel4e732992015-10-16 10:17:21 -070038 for dirpath, dirnames, filenames in fs.walk(
maruel4b14f042015-10-06 12:08:08 -070039 self._tempdir, topdown=True):
40 for filename in filenames:
41 file_path.set_read_only(os.path.join(dirpath, filename), False)
42 for dirname in dirnames:
43 file_path.set_read_only(os.path.join(dirpath, dirname), False)
44 file_path.rmtree(self._tempdir)
45 finally:
46 super(FilePathTest, self).tearDown()
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -040047
48 @property
49 def tempdir(self):
50 if not self._tempdir:
Marc-Antoine Ruel0eb2eb22019-01-29 21:00:16 +000051 self._tempdir = tempfile.mkdtemp(prefix=u'file_path_test')
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -040052 return self._tempdir
53
vadimshe42aeba2016-06-03 12:32:21 -070054 def test_atomic_replace_new_file(self):
55 path = os.path.join(self.tempdir, 'new_file')
56 file_path.atomic_replace(path, 'blah')
57 with open(path, 'rb') as f:
58 self.assertEqual('blah', f.read())
59 self.assertEqual([u'new_file'], os.listdir(self.tempdir))
60
61 def test_atomic_replace_existing_file(self):
62 path = os.path.join(self.tempdir, 'existing_file')
63 with open(path, 'wb') as f:
64 f.write('existing body')
65 file_path.atomic_replace(path, 'new body')
66 with open(path, 'rb') as f:
67 self.assertEqual('new body', f.read())
68 self.assertEqual([u'existing_file'], os.listdir(self.tempdir))
69
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -040070 def assertFileMode(self, filepath, mode, umask=None):
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +000071 umask = test_env.umask() if umask is None else umask
maruel4e732992015-10-16 10:17:21 -070072 actual = fs.stat(filepath).st_mode
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -040073 expected = mode & ~umask
74 self.assertEqual(
75 expected,
76 actual,
77 (filepath, oct(expected), oct(actual), oct(umask)))
78
79 def assertMaskedFileMode(self, filepath, mode):
80 """It's usually when the file was first marked read only."""
81 self.assertFileMode(filepath, mode, 0 if sys.platform == 'win32' else 077)
82
maruel@chromium.org561d4b22013-09-26 21:08:08 +000083 def test_native_case_end_with_os_path_sep(self):
84 # Make sure the trailing os.path.sep is kept.
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +000085 path = file_path.get_native_path_case(test_env.CLIENT_DIR) + os.path.sep
maruel@chromium.org561d4b22013-09-26 21:08:08 +000086 self.assertEqual(file_path.get_native_path_case(path), path)
87
88 def test_native_case_end_with_dot_os_path_sep(self):
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +000089 path = file_path.get_native_path_case(test_env.CLIENT_DIR + os.path.sep)
maruel@chromium.org561d4b22013-09-26 21:08:08 +000090 self.assertEqual(
91 file_path.get_native_path_case(path + '.' + os.path.sep),
92 path)
93
94 def test_native_case_non_existing(self):
95 # Make sure it doesn't throw on non-existing files.
96 non_existing = 'trace_input_test_this_file_should_not_exist'
97 path = os.path.expanduser('~/' + non_existing)
98 self.assertFalse(os.path.exists(path))
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +000099 path = file_path.get_native_path_case(test_env.CLIENT_DIR) + os.path.sep
maruel@chromium.org561d4b22013-09-26 21:08:08 +0000100 self.assertEqual(file_path.get_native_path_case(path), path)
101
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400102 def test_delete_wd_rf(self):
103 # Confirms that a RO file in a RW directory can be deleted on non-Windows.
104 dir_foo = os.path.join(self.tempdir, 'foo')
105 file_bar = os.path.join(dir_foo, 'bar')
maruel4e732992015-10-16 10:17:21 -0700106 fs.mkdir(dir_foo, 0777)
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400107 write_content(file_bar, 'bar')
108 file_path.set_read_only(dir_foo, False)
109 file_path.set_read_only(file_bar, True)
110 self.assertFileMode(dir_foo, 040777)
111 self.assertMaskedFileMode(file_bar, 0100444)
112 if sys.platform == 'win32':
113 # On Windows, a read-only file can't be deleted.
114 with self.assertRaises(OSError):
maruel4e732992015-10-16 10:17:21 -0700115 fs.remove(file_bar)
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400116 else:
maruel4e732992015-10-16 10:17:21 -0700117 fs.remove(file_bar)
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400118
119 def test_delete_rd_wf(self):
120 # Confirms that a Rw file in a RO directory can be deleted on Windows only.
121 dir_foo = os.path.join(self.tempdir, 'foo')
122 file_bar = os.path.join(dir_foo, 'bar')
maruel4e732992015-10-16 10:17:21 -0700123 fs.mkdir(dir_foo, 0777)
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400124 write_content(file_bar, 'bar')
125 file_path.set_read_only(dir_foo, True)
126 file_path.set_read_only(file_bar, False)
127 self.assertMaskedFileMode(dir_foo, 040555)
128 self.assertFileMode(file_bar, 0100666)
129 if sys.platform == 'win32':
130 # A read-only directory has a convoluted meaning on Windows, it means that
131 # the directory is "personalized". This is used as a signal by Windows
132 # Explorer to tell it to look into the directory for desktop.ini.
133 # See http://support.microsoft.com/kb/326549 for more details.
134 # As such, it is important to not try to set the read-only bit on
135 # directories on Windows since it has no effect other than trigger
136 # Windows Explorer to look for desktop.ini, which is unnecessary.
maruel4e732992015-10-16 10:17:21 -0700137 fs.remove(file_bar)
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400138 else:
139 with self.assertRaises(OSError):
maruel4e732992015-10-16 10:17:21 -0700140 fs.remove(file_bar)
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400141
142 def test_delete_rd_rf(self):
143 # Confirms that a RO file in a RO directory can't be deleted.
144 dir_foo = os.path.join(self.tempdir, 'foo')
145 file_bar = os.path.join(dir_foo, 'bar')
maruel4e732992015-10-16 10:17:21 -0700146 fs.mkdir(dir_foo, 0777)
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400147 write_content(file_bar, 'bar')
148 file_path.set_read_only(dir_foo, True)
149 file_path.set_read_only(file_bar, True)
150 self.assertMaskedFileMode(dir_foo, 040555)
151 self.assertMaskedFileMode(file_bar, 0100444)
152 with self.assertRaises(OSError):
153 # It fails for different reason depending on the OS. See the test cases
154 # above.
maruel4e732992015-10-16 10:17:21 -0700155 fs.remove(file_bar)
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400156
157 def test_hard_link_mode(self):
158 # Creates a hard link, see if the file mode changed on the node or the
159 # directory entry.
160 dir_foo = os.path.join(self.tempdir, 'foo')
161 file_bar = os.path.join(dir_foo, 'bar')
162 file_link = os.path.join(dir_foo, 'link')
maruel4e732992015-10-16 10:17:21 -0700163 fs.mkdir(dir_foo, 0777)
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400164 write_content(file_bar, 'bar')
165 file_path.hardlink(file_bar, file_link)
166 self.assertFileMode(file_bar, 0100666)
167 self.assertFileMode(file_link, 0100666)
168 file_path.set_read_only(file_bar, True)
169 self.assertMaskedFileMode(file_bar, 0100444)
170 self.assertMaskedFileMode(file_link, 0100444)
171 # This is bad news for Windows; on Windows, the file must be writeable to be
172 # deleted, but the file node is modified. This means that every hard links
173 # must be reset to be read-only after deleting one of the hard link
174 # directory entry.
175
Takuto Ikutae3f70382019-04-03 14:52:06 +0000176 def test_ensure_tree(self):
177 dir_foo = os.path.join(self.tempdir, 'foo')
178 file_path.ensure_tree(dir_foo, 0777)
179
180 self.assertTrue(os.path.isdir(dir_foo))
181
182 # Do not raise OSError with errno.EEXIST
183 file_path.ensure_tree(dir_foo, 0777)
184
Marc-Antoine Ruel0a795bd2015-01-16 20:32:10 -0500185 def test_rmtree_unicode(self):
186 subdir = os.path.join(self.tempdir, 'hi')
maruel4e732992015-10-16 10:17:21 -0700187 fs.mkdir(subdir)
Marc-Antoine Ruel0a795bd2015-01-16 20:32:10 -0500188 filepath = os.path.join(
189 subdir, u'\u0627\u0644\u0635\u064A\u0646\u064A\u0629')
maruel4e732992015-10-16 10:17:21 -0700190 with fs.open(filepath, 'wb') as f:
Marc-Antoine Ruel0a795bd2015-01-16 20:32:10 -0500191 f.write('hi')
192 # In particular, it fails when the input argument is a str.
193 file_path.rmtree(str(subdir))
194
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400195 if sys.platform == 'darwin':
196 def test_native_case_symlink_wrong_case(self):
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +0000197 base_dir = file_path.get_native_path_case(test_env.TESTS_DIR)
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400198 trace_inputs_dir = os.path.join(base_dir, 'trace_inputs')
199 actual = file_path.get_native_path_case(trace_inputs_dir)
200 self.assertEqual(trace_inputs_dir, actual)
201
202 # Make sure the symlink is not resolved.
203 data = os.path.join(trace_inputs_dir, 'Files2')
204 actual = file_path.get_native_path_case(data)
205 self.assertEqual(
206 os.path.join(trace_inputs_dir, 'files2'), actual)
207
208 data = os.path.join(trace_inputs_dir, 'Files2', '')
209 actual = file_path.get_native_path_case(data)
210 self.assertEqual(
211 os.path.join(trace_inputs_dir, 'files2', ''), actual)
212
213 data = os.path.join(trace_inputs_dir, 'Files2', 'Child1.py')
214 actual = file_path.get_native_path_case(data)
215 # TODO(maruel): Should be child1.py.
216 self.assertEqual(
217 os.path.join(trace_inputs_dir, 'files2', 'Child1.py'), actual)
218
maruel@chromium.org561d4b22013-09-26 21:08:08 +0000219 if sys.platform in ('darwin', 'win32'):
220 def test_native_case_not_sensitive(self):
221 # The home directory is almost guaranteed to have mixed upper/lower case
222 # letters on both Windows and OSX.
223 # This test also ensures that the output is independent on the input
224 # string case.
225 path = os.path.expanduser(u'~')
226 self.assertTrue(os.path.isdir(path))
227 path = path.replace('/', os.path.sep)
228 if sys.platform == 'win32':
229 # Make sure the drive letter is upper case for consistency.
230 path = path[0].upper() + path[1:]
231 # This test assumes the variable is in the native path case on disk, this
232 # should be the case. Verify this assumption:
233 self.assertEqual(path, file_path.get_native_path_case(path))
234 self.assertEqual(
235 file_path.get_native_path_case(path.lower()),
236 file_path.get_native_path_case(path.upper()))
237
238 def test_native_case_not_sensitive_non_existent(self):
239 # This test also ensures that the output is independent on the input
240 # string case.
241 non_existing = os.path.join(
242 'trace_input_test_this_dir_should_not_exist', 'really not', '')
243 path = os.path.expanduser(os.path.join(u'~', non_existing))
244 path = path.replace('/', os.path.sep)
maruel4e732992015-10-16 10:17:21 -0700245 self.assertFalse(fs.exists(path))
maruel@chromium.org561d4b22013-09-26 21:08:08 +0000246 lower = file_path.get_native_path_case(path.lower())
247 upper = file_path.get_native_path_case(path.upper())
248 # Make sure non-existing element is not modified:
249 self.assertTrue(lower.endswith(non_existing.lower()))
250 self.assertTrue(upper.endswith(non_existing.upper()))
251 self.assertEqual(lower[:-len(non_existing)], upper[:-len(non_existing)])
252
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400253 if sys.platform == 'win32':
254 def test_native_case_alternate_datastream(self):
255 # Create the file manually, since tempfile doesn't support ADS.
Marc-Antoine Ruel3c979cb2015-03-11 13:43:28 -0400256 tempdir = unicode(tempfile.mkdtemp(prefix=u'trace_inputs'))
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400257 try:
258 tempdir = file_path.get_native_path_case(tempdir)
259 basename = 'foo.txt'
260 filename = basename + ':Zone.Identifier'
261 filepath = os.path.join(tempdir, filename)
262 open(filepath, 'w').close()
263 self.assertEqual(filepath, file_path.get_native_path_case(filepath))
264 data_suffix = ':$DATA'
265 self.assertEqual(
266 filepath + data_suffix,
267 file_path.get_native_path_case(filepath + data_suffix))
268
269 open(filepath + '$DATA', 'w').close()
270 self.assertEqual(
271 filepath + data_suffix,
272 file_path.get_native_path_case(filepath + data_suffix))
273 # Ensure the ADS weren't created as separate file. You love NTFS, don't
274 # you?
maruel4e732992015-10-16 10:17:21 -0700275 self.assertEqual([basename], fs.listdir(tempdir))
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400276 finally:
maruel4b14f042015-10-06 12:08:08 -0700277 file_path.rmtree(tempdir)
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400278
279 def test_rmtree_win(self):
280 # Mock our sleep for faster test case execution.
281 sleeps = []
282 self.mock(time, 'sleep', sleeps.append)
283 self.mock(sys, 'stderr', StringIO.StringIO())
284
285 # Open a child process, so the file is locked.
286 subdir = os.path.join(self.tempdir, 'to_be_deleted')
maruel4e732992015-10-16 10:17:21 -0700287 fs.mkdir(subdir)
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400288 script = 'import time; open(\'a\', \'w\'); time.sleep(60)'
289 proc = subprocess.Popen([sys.executable, '-c', script], cwd=subdir)
290 try:
291 # Wait until the file exist.
maruel4e732992015-10-16 10:17:21 -0700292 while not fs.isfile(os.path.join(subdir, 'a')):
Marc-Antoine Ruele4ad07e2014-10-15 20:22:29 -0400293 self.assertEqual(None, proc.poll())
294 file_path.rmtree(subdir)
295 self.assertEqual([2, 4, 2], sleeps)
296 # sys.stderr.getvalue() would return a fair amount of output but it is
297 # not completely deterministic so we're not testing it here.
298 finally:
299 proc.wait()
300
Marc-Antoine Ruela275b292014-11-25 15:17:21 -0500301 def test_filter_processes_dir_win(self):
302 python_dir = os.path.dirname(sys.executable)
Marc-Antoine Ruel0eb2eb22019-01-29 21:00:16 +0000303 processes = file_path._filter_processes_dir_win(
304 file_path._enum_processes_win(), python_dir)
Marc-Antoine Ruela275b292014-11-25 15:17:21 -0500305 self.assertTrue(processes)
306 proc_names = [proc.ExecutablePath for proc in processes]
307 # Try to find at least one python process.
308 self.assertTrue(
309 any(proc == sys.executable for proc in proc_names), proc_names)
310
311 def test_filter_processes_tree_win(self):
312 # Create a grand-child.
313 script = (
314 'import subprocess,sys;'
315 'proc = subprocess.Popen('
316 '[sys.executable, \'-u\', \'-c\', \'import time; print(1); '
317 'time.sleep(60)\'], stdout=subprocess.PIPE); '
318 # Signal grand child is ready.
319 'print(proc.stdout.read(1)); '
320 # Wait for parent to have completed the test.
321 'sys.stdin.read(1); '
322 'proc.kill()'
323 )
324 proc = subprocess.Popen(
325 [sys.executable, '-u', '-c', script],
326 stdin=subprocess.PIPE,
327 stdout=subprocess.PIPE)
328 try:
329 proc.stdout.read(1)
330 processes = file_path.filter_processes_tree_win(
Marc-Antoine Ruel0eb2eb22019-01-29 21:00:16 +0000331 file_path._enum_processes_win())
Marc-Antoine Ruela275b292014-11-25 15:17:21 -0500332 self.assertEqual(3, len(processes), processes)
333 proc.stdin.write('a')
334 proc.wait()
335 except Exception:
336 proc.kill()
337 finally:
338 proc.wait()
339
maruel@chromium.org561d4b22013-09-26 21:08:08 +0000340 if sys.platform != 'win32':
341 def test_symlink(self):
342 # This test will fail if the checkout is in a symlink.
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +0000343 actual = file_path.split_at_symlink(None, test_env.CLIENT_DIR)
344 expected = (test_env.CLIENT_DIR, None, None)
maruel@chromium.org561d4b22013-09-26 21:08:08 +0000345 self.assertEqual(expected, actual)
346
347 actual = file_path.split_at_symlink(
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +0000348 None, os.path.join(test_env.TESTS_DIR, 'trace_inputs'))
349 expected = (os.path.join(test_env.TESTS_DIR, 'trace_inputs'), None, None)
350 self.assertEqual(expected, actual)
351
352 actual = file_path.split_at_symlink(
353 None, os.path.join(test_env.TESTS_DIR, 'trace_inputs', 'files2'))
maruel@chromium.org561d4b22013-09-26 21:08:08 +0000354 expected = (
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +0000355 os.path.join(test_env.TESTS_DIR, 'trace_inputs'), 'files2', '')
maruel@chromium.org561d4b22013-09-26 21:08:08 +0000356 self.assertEqual(expected, actual)
357
358 actual = file_path.split_at_symlink(
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +0000359 test_env.CLIENT_DIR, os.path.join('tests', 'trace_inputs', 'files2'))
maruel@chromium.org561d4b22013-09-26 21:08:08 +0000360 expected = (
361 os.path.join('tests', 'trace_inputs'), 'files2', '')
362 self.assertEqual(expected, actual)
363 actual = file_path.split_at_symlink(
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +0000364 test_env.CLIENT_DIR,
365 os.path.join('tests', 'trace_inputs', 'files2', 'bar'))
366 expected = (os.path.join('tests', 'trace_inputs'), 'files2', '/bar')
maruel@chromium.org561d4b22013-09-26 21:08:08 +0000367 self.assertEqual(expected, actual)
368
369 def test_native_case_symlink_right_case(self):
370 actual = file_path.get_native_path_case(
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +0000371 os.path.join(test_env.TESTS_DIR, 'trace_inputs'))
maruel@chromium.org561d4b22013-09-26 21:08:08 +0000372 self.assertEqual('trace_inputs', os.path.basename(actual))
373
374 # Make sure the symlink is not resolved.
375 actual = file_path.get_native_path_case(
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +0000376 os.path.join(test_env.TESTS_DIR, 'trace_inputs', 'files2'))
maruel@chromium.org561d4b22013-09-26 21:08:08 +0000377 self.assertEqual('files2', os.path.basename(actual))
378
maruel9cdd7612015-12-02 13:40:52 -0800379 else:
380 def test_undeleteable_chmod(self):
381 # Create a file and a directory with an empty ACL. Then try to delete it.
382 dirpath = os.path.join(self.tempdir, 'd')
383 filepath = os.path.join(dirpath, 'f')
384 os.mkdir(dirpath)
385 with open(filepath, 'w') as f:
386 f.write('hi')
387 os.chmod(filepath, 0)
388 os.chmod(dirpath, 0)
389 file_path.rmtree(dirpath)
390
391 def test_undeleteable_owner(self):
392 # Create a file and a directory with an empty ACL. Then try to delete it.
393 dirpath = os.path.join(self.tempdir, 'd')
394 filepath = os.path.join(dirpath, 'f')
395 os.mkdir(dirpath)
396 with open(filepath, 'w') as f:
397 f.write('hi')
398 import win32security
399 user, _domain, _type = win32security.LookupAccountName(
400 '', getpass.getuser())
401 sd = win32security.SECURITY_DESCRIPTOR()
402 sd.Initialize()
403 sd.SetSecurityDescriptorOwner(user, False)
404 # Create an empty DACL, which removes all rights.
405 dacl = win32security.ACL()
406 dacl.Initialize()
407 sd.SetSecurityDescriptorDacl(1, dacl, 0)
408 win32security.SetFileSecurity(
409 fs.extend(filepath), win32security.DACL_SECURITY_INFORMATION, sd)
410 win32security.SetFileSecurity(
411 fs.extend(dirpath), win32security.DACL_SECURITY_INFORMATION, sd)
412 file_path.rmtree(dirpath)
413
maruel@chromium.org561d4b22013-09-26 21:08:08 +0000414
415if __name__ == '__main__':
Marc-Antoine Ruel76cfcee2019-04-01 23:16:36 +0000416 test_env.main()