Repository: google/openhtf

Issue:
Attaching binary file using test.attach raises UnicodeDecodeError
If I attach a png or avi I see the following in OutputTestRecord

Python2.7/site-packages/openhtf/**init**.py", line 185, in OutputTestRecord
    output_cb(test_record)
  File "virtualenv/local/lib/python2.7/site-packages/openhtf/**init**.py", line 83, in **call**
    f.write(self.encode(as_dict))
  File "/usr/lib/python2.7/json/encoder.py", line 209, in encode
    chunks = list(chunks)
  File "/usr/lib/python2.7/json/encoder.py", line 434, in _iterencode
    for chunk in _iterencode_dict(o, _current_indent_level):
  File "/usr/lib/python2.7/json/encoder.py", line 408, in _iterencode_dict
    for chunk in chunks:
  File "/usr/lib/python2.7/json/encoder.py", line 332, in _iterencode_list
    for chunk in chunks:
  File "/usr/lib/python2.7/json/encoder.py", line 408, in _iterencode_dict
    for chunk in chunks:
  File "/usr/lib/python2.7/json/encoder.py", line 408, in _iterencode_dict
    for chunk in chunks:
  File "/usr/lib/python2.7/json/encoder.py", line 390, in _iterencode_dict
    yield _encoder(value)
UnicodeDecodeError: 'utf8' codec can't decode byte 0x89 in position 0: invalid start byte



Discussion:
JSON doesn't do binary, so you'll have to base64 encode the attachment or use an output method that does handle binary data.


Resolving patch:
diff --git a/openhtf/io/output/json_factory.py b/openhtf/io/output/json_factory.py
--- a/openhtf/io/output/json_factory.py
+++ b/openhtf/io/output/json_factory.py
@@ -1,5 +1,6 @@
 """Module for outputting test record to JSON-formatted files."""
 
+import base64
 from json import JSONEncoder
 
 from openhtf import util
@@ -21,7 +22,9 @@ class OutputToJSON(JSONEncoder):
     filename_pattern: A format string specifying the filename to write to,
       will be formatted with the Test Record as a dictionary.
     inline_attachments: Whether attachments should be included inline in the
-      output.  Set to False if you expect to have large binary attachments.
+      output. Set to False if you expect to have large binary attachments. If
+      True (the default), then attachments are base64 encoded to allow for
+      binary data that's not supported by JSON directly.
   """
 
   def __init__(self, filename_pattern=None, inline_attachments=True, **kwargs):
@@ -40,6 +43,9 @@ def __call__(self, test_record):
     assert self.filename_pattern, 'filename_pattern required'
     if self.inline_attachments:
       as_dict = util.ConvertToBaseTypes(test_record)
+      for phase in as_dict['phases']:
+        for value in phase['attachments'].itervalues():
+          value['data'] = base64.standard_b64encode(value['data'])
     else:
       as_dict = util.ConvertToBaseTypes(test_record, ignore_keys='attachments')
     with open(self.filename_pattern % as_dict, 'w') as f:


Test patch:
diff --git a/test/io/output/record.json b/test/io/output/record.json
--- a/test/io/output/record.json
+++ b/test/io/output/record.json
@@ -22,7 +22,7 @@
     {
       "attachments": {
         "test_attachment": {
-          "data": "This is test attachment data.", 
+          "data": "VGhpcyBpcyB0ZXN0IGF0dGFjaG1lbnQgZGF0YS4=", 
           "mimetype": null
         }
       }, 
@@ -82,4 +82,4 @@
   ], 
   "start_time_millis": "12344", 
   "station_id": "unittest_openhtf"
-}
\ No newline at end of file
+}