Skip to content

Commit

Permalink
OpenHTF: Guess mimetype from attachment name.
Browse files Browse the repository at this point in the history
Rationale: In attach_from_file() we try to guess mimetype from the source filename. It has sometimes surprised users that the mimetype is not inferred from the attachment name as well.

TESTED=
Before: `complete_test_output.txt` gets `type: BINARY` (integer=5)
e.g. https://www.mfg-inspector.com/pr/chauffeur/testrun/chauffeur/QQABTkEAAQYBY0GFCrI=.raw
After: `complete_test_output.txt` gets `type: TEXT_UTF8` (integer=7)
e.g. https://www.mfg-inspector.com/pr/chauffeur/testrun/chauffeur/QQABTkEAAQYBY0arU1Q=.raw

The attachment `complete_test_output.txt` is attached using `test.attach_from_file(open_file.name, 'complete_test_output.txt')`. (In this case, open_file.name is something like `/tmp/asdfljk` and has no extension.)
https://cs.corp.google.com/piper///depot/google3/third_party/car/hw/testing/framework/logs.py?rcl=195879010&l=125

PiperOrigin-RevId: 196022999
  • Loading branch information
do-not-reply authored and Kenadia committed May 14, 2018
1 parent 0723912 commit c3cf601
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 9 deletions.
30 changes: 22 additions & 8 deletions openhtf/core/test_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@

_LOG = logging.getLogger(__name__)

# Sentinel value indicating that the mimetype should be inferred.
INFER_MIMETYPE = object()


class BlankDutIdError(Exception):
"""DUT serial cannot be blank at the end of a test."""
Expand Down Expand Up @@ -472,45 +475,56 @@ def result(self, result):
def attachments(self):
return self.phase_record.attachments

def attach(self, name, data, mimetype=None):
def attach(self, name, data, mimetype=INFER_MIMETYPE):
"""Store the given data as an attachment with the given name.
Args:
name: Attachment name under which to store this data.
data: Data to attach.
mimetype: If provided, will be saved in the attachment.
mimetype: One of the following:
INFER_MIMETYPE: The type will be guessed from the attachment name.
None: The type will be left unspecified.
A string: The type will be set to the specified value.
Raises:
DuplicateAttachmentError: Raised if there is already an attachment with
the given name.
"""
if name in self.phase_record.attachments:
raise DuplicateAttachmentError('Duplicate attachment for %s' % name)
if mimetype and not mimetypes.guess_extension(mimetype):

if mimetype is INFER_MIMETYPE:
mimetype = mimetypes.guess_type(name)[0]
elif mimetype is not None and not mimetypes.guess_extension(mimetype):
_LOG.warning('Unrecognized MIME type: "%s" for attachment "%s"',
mimetype, name)

self.phase_record.attachments[name] = test_record.Attachment(data, mimetype)

def attach_from_file(self, filename, name=None, mimetype=None):
def attach_from_file(self, filename, name=None, mimetype=INFER_MIMETYPE):
"""Store the contents of the given filename as an attachment.
Args:
filename: The file to read data from to attach.
name: If provided, override the attachment name, otherwise it will
default to the filename.
mimetype: If provided, override the attachment mime type, otherwise the
mime type will be guessed based on the file extension.
mimetype: One of the following:
INFER_MIMETYPE: The type will be guessed first, from the file name,
and second (i.e. as a fallback), from the attachment name.
None: The type will be left unspecified.
A string: The type will be set to the specified value.
Raises:
DuplicateAttachmentError: Raised if there is already an attachment with
the given name.
IOError: Raised if the given filename couldn't be opened.
"""
if mimetype is INFER_MIMETYPE:
mimetype = mimetypes.guess_type(filename)[0] or mimetype
with open(filename, 'rb') as f: # pylint: disable=invalid-name
self.attach(
name if name is not None else os.path.basename(filename), f.read(),
mimetype=mimetype if mimetype is not None else mimetypes.guess_type(
filename)[0])
mimetype=mimetype)

def _finalize_measurements(self):
"""Perform end-of-phase finalization steps for measurements.
Expand Down
21 changes: 20 additions & 1 deletion test/test_state_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import tempfile
import unittest

import mock

import openhtf
from openhtf.core import test_state
from openhtf.core import test_descriptor
from openhtf.core import test_state


@openhtf.measures('test_measurement')
Expand Down Expand Up @@ -67,3 +68,21 @@ def test_get_measurement_immutable(self):

measurement.value.append(4)
self.assertNotEqual(measurement_val, measurement.value)

def test_infer_mime_type_from_file_name(self):
with tempfile.NamedTemporaryFile(suffix='.txt') as f:
f.write('Mock text contents.')
f.flush()
file_name = f.name
self.test_api.attach_from_file(file_name, 'attachment')
attachment = self.test_api.get_attachment('attachment')
self.assertEqual(attachment.mimetype, 'text/plain')

def test_infer_mime_type_from_attachment_name(self):
with tempfile.NamedTemporaryFile() as f:
f.write('Mock text contents.')
f.flush()
file_name = f.name
self.test_api.attach_from_file(file_name, 'attachment.png')
attachment = self.test_api.get_attachment('attachment.png')
self.assertEqual(attachment.mimetype, 'image/png')

0 comments on commit c3cf601

Please sign in to comment.