Index: build/android/pylib/base/output_manager.py |
diff --git a/build/android/pylib/base/output_manager.py b/build/android/pylib/base/output_manager.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4a757da22b1a85a7234677a156a34930d01fe405 |
--- /dev/null |
+++ b/build/android/pylib/base/output_manager.py |
@@ -0,0 +1,110 @@ |
+# Copyright 2017 The Chromium Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+import logging |
+import os |
+ |
+from devil.utils import reraiser_thread |
+ |
+ |
+class Datatype(object): |
+ HTML = 'html' |
+ IMAGE = 'image' |
+ TEXT = 'text' |
+ |
+ |
+class OutputManager(object): |
+ |
+ def __init__(self): |
+ """OutputManager Constructor. |
+ |
+ This class provides a simple interface to save test output. Subclasses |
+ of this will allow users to save test results in the cloud or locally. |
+ """ |
+ self._allow_upload = False |
jbudorick
2017/08/10 16:27:37
nit: maybe allow_save or allow_archive, since this
|
+ self._thread_group = None |
+ |
+ def ArchiveAndDeleteFile( |
+ self, in_filepath, out_filename, out_subdir, datatype=Datatype.TEXT): |
+ """Archive file contents asynchonously and then deletes file. |
+ |
+ Example Usage. |
+ |
+ try: |
+ // Create non-temporary File. Upload is done asynchonously. |
+ file = ... |
+ file.write(...) |
+ finally: |
+ output_manager.ArchiveAndDeleteFile(...) |
+ |
+ Args: |
+ in_filepath: Path for file you want to archive. |
+ out_filename: Name for saved file. |
+ out_subdir: Directory to save |out_file| in. |
+ datatype: Datatype of file. |
+ |
+ Returns: |
+ A link to where the contents of the file will be archived. |
+ """ |
+ if not self._allow_upload: |
+ raise Exception('Must run |SetUp| before attempting to upload!') |
+ |
+ job = self._CreateArchiveJob( |
+ in_filepath, out_filename, out_subdir, datatype) |
+ |
+ if job is None: |
+ return '' |
+ |
+ def archive(): |
+ try: |
+ job.Archive() |
+ finally: |
+ os.remove(in_filepath) |
+ |
+ thread = reraiser_thread.ReraiserThread(func=archive) |
+ thread.start() |
+ self._thread_group.Add(thread) |
+ return job.Link() |
+ |
+ def _CreateArchiveJob(self, in_filepath, out_filename, out_subdir, datatype): |
+ """Returns an instance of Job that actually uploads/saves the file.""" |
+ raise NotImplementedError |
+ |
+ def SetUp(self): |
+ self._allow_upload = True |
+ self._thread_group = reraiser_thread.ReraiserThreadGroup() |
+ |
+ def TearDown(self): |
+ self._allow_upload = False |
+ logging.info('Finishing archiving output.') |
+ self._thread_group.JoinAll() |
+ |
+ def __enter__(self): |
+ self.SetUp() |
+ return self |
+ |
+ def __exit__(self, _exc_type, _exc_val, _exc_tb): |
+ self.TearDown() |
+ |
+ |
+class Job(object): |
+ |
+ def __init__(self, in_filepath, out_filename, out_subdir, datatype): |
+ self._in_filepath = in_filepath |
+ self._out_filename = out_filename |
+ self._out_subdir = out_subdir |
+ self._datatype = datatype |
+ |
+ def Link(self): |
+ """Returns location of archived file. |
+ |
+ Note that this function will almost certainly be used before the file |
+ has finished being archived. Therefore, this needs to know the exact |
+ location of the archived file before it is archived. |
+ """ |
+ raise NotImplementedError |
+ |
+ def Archive(self): |
+ """Archives file.""" |
+ raise NotImplementedError |