Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Side by Side Diff: testing_support/trial_dir.py

Issue 939523004: Copied files from depot_tools. (Closed) Base URL: https://chromium.googlesource.com/infra/testing/testing_support.git@master
Patch Set: Added some tests Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « testing_support/tests/trial_dir_test.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5
6 import atexit
7 import logging
8 import os
9 import stat
10 import subprocess
11 import sys
12 import tempfile
13 import time
14
15 from testing_support import auto_stub
16
17
18 def rmtree(path):
19 """shutil.rmtree() on steroids.
20
21 Recursively removes a directory, even if it's marked read-only.
22
23 shutil.rmtree() doesn't work on Windows if any of the files or directories
24 are read-only, which svn repositories and some .svn files are. We need to
25 be able to force the files to be writable (i.e., deletable) as we traverse
26 the tree.
27
28 Even with all this, Windows still sometimes fails to delete a file, citing
29 a permission error (maybe something to do with antivirus scans or disk
30 indexing). The best suggestion any of the user forums had was to wait a
31 bit and try again, so we do that too. It's hand-waving, but sometimes it
32 works. :/
33
34 On POSIX systems, things are a little bit simpler. The modes of the files
35 to be deleted doesn't matter, only the modes of the directories containing
36 them are significant. As the directory tree is traversed, each directory
37 has its mode set appropriately before descending into it. This should
38 result in the entire tree being removed, with the possible exception of
39 *path itself, because nothing attempts to change the mode of its parent.
40 Doing so would be hazardous, as it's not a directory slated for removal.
41 In the ordinary case, this is not a problem: for our purposes, the user
42 will never lack write permission on *path's parent.
43 """
44 if not os.path.exists(path):
45 return
46
47 if os.path.islink(path) or not os.path.isdir(path):
48 raise ValueError('Called rmtree(%s) in non-directory' % path)
49
50 if sys.platform == 'win32':
51 # Give up and use cmd.exe's rd command.
52 path = os.path.normcase(path)
53 for _ in xrange(3):
54 exitcode = subprocess.call(['cmd.exe', '/c', 'rd', '/q', '/s', path])
55 if exitcode == 0:
56 return
57 else:
58 print >> sys.stderr, 'rd exited with code %d' % exitcode
59 time.sleep(3)
60 raise Exception('Failed to remove path %s' % path)
61
62 # On POSIX systems, we need the x-bit set on the directory to access it,
63 # the r-bit to see its contents, and the w-bit to remove files from it.
64 # The actual modes of the files within the directory is irrelevant.
65 os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
66
67 def remove(func, subpath):
68 func(subpath)
69
70 for fn in os.listdir(path):
71 # If fullpath is a symbolic link that points to a directory, isdir will
72 # be True, but we don't want to descend into that as a directory, we just
73 # want to remove the link. Check islink and treat links as ordinary files
74 # would be treated regardless of what they reference.
75 fullpath = os.path.join(path, fn)
76 if os.path.islink(fullpath) or not os.path.isdir(fullpath):
77 remove(os.remove, fullpath)
78 else:
79 # Recurse.
80 rmtree(fullpath)
81
82 remove(os.rmdir, path)
83
84
85 class TrialDir(object):
86 """Manages a temporary directory.
87
88 On first object creation, TrialDir.TRIAL_ROOT will be set to a new temporary
89 directory created in /tmp or the equivalent. It will be deleted on process
90 exit unless TrialDir.SHOULD_LEAK is set to True.
91 """
92 # When SHOULD_LEAK is set to True, temporary directories created while the
93 # tests are running aren't deleted at the end of the tests. Expect failures
94 # when running more than one test due to inter-test side-effects. Helps with
95 # debugging.
96 SHOULD_LEAK = False
97
98 # Main root directory.
99 TRIAL_ROOT = None
100
101 def __init__(self, subdir, leak=False):
102 self.leak = self.SHOULD_LEAK or leak
103 self.subdir = subdir
104 self.root_dir = None
105
106 def set_up(self):
107 """All late initialization comes here."""
108 # You can override self.TRIAL_ROOT.
109 if not self.TRIAL_ROOT:
110 # Was not yet initialized.
111 TrialDir.TRIAL_ROOT = os.path.realpath(tempfile.mkdtemp(prefix='trial'))
112 atexit.register(self._clean)
113 self.root_dir = os.path.join(TrialDir.TRIAL_ROOT, self.subdir)
114 rmtree(self.root_dir)
115 os.makedirs(self.root_dir)
116
117 def tear_down(self):
118 """Cleans the trial subdirectory for this instance."""
119 if not self.leak:
120 logging.debug('Removing %s' % self.root_dir)
121 rmtree(self.root_dir)
122 else:
123 logging.error('Leaking %s' % self.root_dir)
124 self.root_dir = None
125
126 @staticmethod
127 def _clean():
128 """Cleans the root trial directory."""
129 if not TrialDir.SHOULD_LEAK:
130 logging.debug('Removing %s' % TrialDir.TRIAL_ROOT)
131 rmtree(TrialDir.TRIAL_ROOT)
132 else:
133 logging.error('Leaking %s' % TrialDir.TRIAL_ROOT)
134
135
136 class TrialDirMixIn(object):
137 def setUp(self):
138 # Create a specific directory just for the test.
139 self.trial = TrialDir(self.id())
140 self.trial.set_up()
141
142 def tearDown(self):
143 self.trial.tear_down()
144
145 @property
146 def root_dir(self):
147 return self.trial.root_dir
148
149
150 class TestCase(auto_stub.TestCase, TrialDirMixIn):
151 """Base unittest class that cleans off a trial directory in tearDown()."""
152 def setUp(self):
153 auto_stub.TestCase.setUp(self)
154 TrialDirMixIn.setUp(self)
155
156 def tearDown(self):
157 TrialDirMixIn.tearDown(self)
158 auto_stub.TestCase.tearDown(self)
OLDNEW
« no previous file with comments | « testing_support/tests/trial_dir_test.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698