OLD | NEW |
| (Empty) |
1 # Copyright 2014 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 from recipe_engine import recipe_api | |
6 from recipe_engine import util as recipe_util | |
7 | |
8 import os | |
9 import shutil | |
10 import tempfile | |
11 | |
12 | |
13 class InputDataPlaceholder(recipe_util.Placeholder): | |
14 def __init__(self, data, suffix): | |
15 assert isinstance(data, basestring) | |
16 self.data = data | |
17 self.suffix = suffix | |
18 self._backing_file = None | |
19 super(InputDataPlaceholder, self).__init__() | |
20 | |
21 @property | |
22 def backing_file(self): | |
23 return self._backing_file | |
24 | |
25 def render(self, test): | |
26 assert not self._backing_file, 'Placeholder can be used only once' | |
27 if test.enabled: | |
28 # cheat and pretend like we're going to pass the data on the | |
29 # cmdline for test expectation purposes. | |
30 self._backing_file = self.data | |
31 else: # pragma: no cover | |
32 input_fd, self._backing_file = tempfile.mkstemp(suffix=self.suffix) | |
33 os.write(input_fd, self.data) | |
34 os.close(input_fd) | |
35 return [self._backing_file] | |
36 | |
37 def result(self, presentation, test): | |
38 assert self._backing_file | |
39 exists = os.path.exists(self._backing_file) | |
40 if not test.enabled and exists: # pragma: no cover | |
41 os.unlink(self._backing_file) | |
42 self._backing_file = None | |
43 | |
44 | |
45 class OutputDataPlaceholder(recipe_util.Placeholder): | |
46 def __init__(self, suffix, leak_to): | |
47 self.suffix = suffix | |
48 self.leak_to = leak_to | |
49 self._backing_file = None | |
50 super(OutputDataPlaceholder, self).__init__() | |
51 | |
52 @property | |
53 def backing_file(self): | |
54 return self._backing_file | |
55 | |
56 def render(self, test): | |
57 assert not self._backing_file, 'Placeholder can be used only once' | |
58 if self.leak_to: | |
59 self._backing_file = str(self.leak_to) | |
60 return [self._backing_file] | |
61 if test.enabled: | |
62 self._backing_file = '/path/to/tmp/' + self.suffix.lstrip('.') | |
63 else: # pragma: no cover | |
64 output_fd, self._backing_file = tempfile.mkstemp(suffix=self.suffix) | |
65 os.close(output_fd) | |
66 return [self._backing_file] | |
67 | |
68 def result(self, presentation, test): | |
69 assert self._backing_file | |
70 if test.enabled: | |
71 self._backing_file = None | |
72 return test.data | |
73 else: # pragma: no cover | |
74 try: | |
75 with open(self._backing_file, 'rb') as f: | |
76 return f.read() | |
77 finally: | |
78 if not self.leak_to: | |
79 os.unlink(self._backing_file) | |
80 self._backing_file = None | |
81 | |
82 | |
83 class OutputDataDirPlaceholder(recipe_util.Placeholder): | |
84 def __init__(self, suffix, leak_to): | |
85 self.suffix = suffix | |
86 self.leak_to = leak_to | |
87 self._backing_dir = None | |
88 super(OutputDataDirPlaceholder, self).__init__() | |
89 | |
90 @property | |
91 def backing_file(self): # pragma: no cover | |
92 raise ValueError('Output dir placeholders can not be used for stdin, ' | |
93 'stdout or stderr') | |
94 | |
95 def render(self, test): | |
96 assert not self._backing_dir, 'Placeholder can be used only once' | |
97 if self.leak_to: | |
98 self._backing_dir = str(self.leak_to) | |
99 return [self._backing_dir] | |
100 if test.enabled: | |
101 self._backing_dir = '/path/to/tmp/' + self.suffix | |
102 else: # pragma: no cover | |
103 self._backing_dir = tempfile.mkdtemp(suffix=self.suffix) | |
104 return [self._backing_dir] | |
105 | |
106 def result(self, presentation, test): | |
107 assert self._backing_dir | |
108 if test.enabled: | |
109 self._backing_dir = None | |
110 return test.data or {} | |
111 else: # pragma: no cover | |
112 try: | |
113 all_files = {} | |
114 for dir_path, _, files in os.walk(self._backing_dir): | |
115 for filename in files: | |
116 abs_path = os.path.join(dir_path, filename) | |
117 rel_path = os.path.relpath(abs_path, self._backing_dir) | |
118 with open(abs_path, 'rb') as f: | |
119 all_files[rel_path] = f.read() | |
120 return all_files | |
121 finally: | |
122 if not self.leak_to: | |
123 shutil.rmtree(self._backing_dir) | |
124 self._backing_dir = None | |
125 | |
126 | |
127 class RawIOApi(recipe_api.RecipeApi): | |
128 @recipe_util.returns_placeholder | |
129 @staticmethod | |
130 def input(data, suffix=''): | |
131 return InputDataPlaceholder(data, suffix) | |
132 | |
133 @recipe_util.returns_placeholder | |
134 @staticmethod | |
135 def output(suffix='', leak_to=None): | |
136 """Returns a Placeholder for use as a step argument, or for std{out,err}. | |
137 | |
138 If 'leak_to' is None, the placeholder is backed by a temporary file with | |
139 a suffix 'suffix'. The file is deleted when the step finishes. | |
140 | |
141 If 'leak_to' is not None, then it should be a Path and placeholder | |
142 redirects IO to a file at that path. Once step finishes, the file is | |
143 NOT deleted (i.e. it's 'leaking'). 'suffix' is ignored in that case. | |
144 """ | |
145 return OutputDataPlaceholder(suffix, leak_to) | |
146 | |
147 @recipe_util.returns_placeholder | |
148 @staticmethod | |
149 def output_dir(suffix='', leak_to=None): | |
150 """Returns a directory Placeholder for use as a step argument. | |
151 | |
152 If 'leak_to' is None, the placeholder is backed by a temporary dir with | |
153 a suffix 'suffix'. The dir is deleted when the step finishes. | |
154 | |
155 If 'leak_to' is not None, then it should be a Path and placeholder | |
156 redirects IO to a dir at that path. Once step finishes, the dir is | |
157 NOT deleted (i.e. it's 'leaking'). 'suffix' is ignored in that case. | |
158 """ | |
159 return OutputDataDirPlaceholder(suffix, leak_to) | |
OLD | NEW |