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 |